summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java16
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobParameters.java1
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobService.java24
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java2
-rw-r--r--core/api/current.txt123
-rw-r--r--core/api/system-current.txt89
-rw-r--r--core/api/test-current.txt9
-rw-r--r--core/java/android/app/ApplicationErrorReport.java18
-rw-r--r--core/java/android/app/ApplicationPackageManager.java32
-rw-r--r--core/java/android/app/BroadcastOptions.java17
-rw-r--r--core/java/android/app/PendingIntent.java63
-rw-r--r--core/java/android/app/SystemServiceRegistry.java7
-rw-r--r--core/java/android/app/UiAutomation.java10
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java19
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutorHelper.java7
-rw-r--r--core/java/android/app/trust/ITrustListener.aidl4
-rw-r--r--core/java/android/app/trust/TrustManager.java22
-rw-r--r--core/java/android/companion/virtual/IVirtualDevice.aidl13
-rw-r--r--core/java/android/companion/virtual/IVirtualDeviceIntentInterceptor.aidl39
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java99
-rw-r--r--core/java/android/content/Context.java97
-rw-r--r--core/java/android/content/pm/IPackageInstallerSession.aidl3
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageInstaller.java60
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/content/pm/UserProperties.java112
-rw-r--r--core/java/android/credentials/CredentialManager.java21
-rw-r--r--core/java/android/hardware/biometrics/IBiometricContextListener.aidl14
-rw-r--r--core/java/android/hardware/input/VirtualKeyboardConfig.java89
-rw-r--r--core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java5
-rw-r--r--core/java/android/net/vcn/VcnManager.java17
-rw-r--r--core/java/android/os/PerformanceHintManager.java20
-rw-r--r--core/java/android/service/credentials/Action.java2
-rw-r--r--core/java/android/service/credentials/BeginCreateCredentialRequest.java20
-rw-r--r--core/java/android/service/credentials/BeginGetCredentialOption.java4
-rw-r--r--core/java/android/service/credentials/BeginGetCredentialRequest.aidl (renamed from core/java/android/service/credentials/BeginGetCredentialsRequest.aidl)2
-rw-r--r--core/java/android/service/credentials/BeginGetCredentialRequest.java (renamed from core/java/android/service/credentials/BeginGetCredentialsRequest.java)64
-rw-r--r--core/java/android/service/credentials/BeginGetCredentialResponse.aidl3
-rw-r--r--core/java/android/service/credentials/BeginGetCredentialResponse.java (renamed from core/java/android/service/credentials/BeginGetCredentialsResponse.java)30
-rw-r--r--core/java/android/service/credentials/BeginGetCredentialsResponse.aidl3
-rw-r--r--core/java/android/service/credentials/CallingAppInfo.java98
-rw-r--r--core/java/android/service/credentials/CreateCredentialRequest.java20
-rw-r--r--core/java/android/service/credentials/CredentialProviderException.java10
-rw-r--r--core/java/android/service/credentials/CredentialProviderService.java39
-rw-r--r--core/java/android/service/credentials/CredentialsResponseContent.java2
-rw-r--r--core/java/android/service/credentials/GetCredentialRequest.java32
-rw-r--r--core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl2
-rw-r--r--core/java/android/service/credentials/IBeginGetCredentialCallback.aidl13
-rw-r--r--core/java/android/service/credentials/IBeginGetCredentialsCallback.aidl13
-rw-r--r--core/java/android/service/credentials/ICredentialProviderService.aidl6
-rw-r--r--core/java/android/service/voice/HotwordAudioStream.java156
-rw-r--r--core/java/android/telephony/TelephonyCallback.java11
-rw-r--r--core/java/android/text/EmojiConsistency.java82
-rw-r--r--core/java/android/view/InputDevice.java96
-rw-r--r--core/java/android/view/WindowManagerImpl.java113
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java24
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java48
-rw-r--r--core/java/android/view/autofill/OWNERS3
-rw-r--r--core/java/android/window/DisplayWindowPolicyController.java3
-rw-r--r--core/java/android/window/WindowMetricsController.java172
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java119
-rw-r--r--core/jni/android_view_InputDevice.cpp23
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/values-af/strings.xml4
-rw-r--r--core/res/res/values-am/strings.xml42
-rw-r--r--core/res/res/values-ar/strings.xml4
-rw-r--r--core/res/res/values-as/strings.xml4
-rw-r--r--core/res/res/values-az/strings.xml4
-rw-r--r--core/res/res/values-b+sr+Latn-television/strings.xml4
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml4174
-rw-r--r--core/res/res/values-be/strings.xml4
-rw-r--r--core/res/res/values-bg/strings.xml4
-rw-r--r--core/res/res/values-bn/strings.xml4
-rw-r--r--core/res/res/values-bs/strings.xml4
-rw-r--r--core/res/res/values-ca/strings.xml4
-rw-r--r--core/res/res/values-cs/strings.xml4
-rw-r--r--core/res/res/values-da/strings.xml4
-rw-r--r--core/res/res/values-de/strings.xml4
-rw-r--r--core/res/res/values-el/strings.xml4
-rw-r--r--core/res/res/values-en-rAU/strings.xml58
-rw-r--r--core/res/res/values-en-rCA/strings.xml58
-rw-r--r--core/res/res/values-en-rGB/strings.xml58
-rw-r--r--core/res/res/values-en-rIN/strings.xml58
-rw-r--r--core/res/res/values-en-rXC/strings.xml58
-rw-r--r--core/res/res/values-es-rUS/strings.xml4
-rw-r--r--core/res/res/values-es/strings.xml4
-rw-r--r--core/res/res/values-et/strings.xml4
-rw-r--r--core/res/res/values-eu/strings.xml4
-rw-r--r--core/res/res/values-fa/strings.xml4
-rw-r--r--core/res/res/values-fi/strings.xml4
-rw-r--r--core/res/res/values-fr-rCA/strings.xml4
-rw-r--r--core/res/res/values-fr/strings.xml4
-rw-r--r--core/res/res/values-gl/strings.xml4
-rw-r--r--core/res/res/values-gu/strings.xml4
-rw-r--r--core/res/res/values-hi/strings.xml4
-rw-r--r--core/res/res/values-hr/strings.xml4
-rw-r--r--core/res/res/values-hu/strings.xml4
-rw-r--r--core/res/res/values-hy/strings.xml4
-rw-r--r--core/res/res/values-in/strings.xml4
-rw-r--r--core/res/res/values-is/strings.xml4
-rw-r--r--core/res/res/values-it/strings.xml4
-rw-r--r--core/res/res/values-iw/strings.xml4
-rw-r--r--core/res/res/values-ja/strings.xml4
-rw-r--r--core/res/res/values-ka/strings.xml4
-rw-r--r--core/res/res/values-kk/strings.xml4
-rw-r--r--core/res/res/values-km/strings.xml4
-rw-r--r--core/res/res/values-kn/strings.xml4
-rw-r--r--core/res/res/values-ko/strings.xml4
-rw-r--r--core/res/res/values-ky/strings.xml4
-rw-r--r--core/res/res/values-lo/strings.xml4
-rw-r--r--core/res/res/values-lt/strings.xml4
-rw-r--r--core/res/res/values-lv/strings.xml4
-rw-r--r--core/res/res/values-mcc001-mnc01-b+sr+Latn/strings.xml2
-rw-r--r--core/res/res/values-mcc310-mnc150-b+sr+Latn/strings.xml2
-rw-r--r--core/res/res/values-mcc310-mnc170-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-mcc310-mnc280-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-mcc310-mnc380-b+sr+Latn/strings.xml4
-rw-r--r--core/res/res/values-mcc310-mnc410-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-mcc310-mnc950-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-mcc311-mnc180-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-mcc312-mnc670-b+sr+Latn/strings.xml2
-rw-r--r--core/res/res/values-mcc313-mnc100-b+sr+Latn/strings.xml2
-rw-r--r--core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-mk/strings.xml4
-rw-r--r--core/res/res/values-ml/strings.xml4
-rw-r--r--core/res/res/values-mn/strings.xml4
-rw-r--r--core/res/res/values-mr/strings.xml4
-rw-r--r--core/res/res/values-ms/strings.xml4
-rw-r--r--core/res/res/values-my/strings.xml4
-rw-r--r--core/res/res/values-nb/strings.xml4
-rw-r--r--core/res/res/values-ne/strings.xml4
-rw-r--r--core/res/res/values-nl/strings.xml4
-rw-r--r--core/res/res/values-or/strings.xml4
-rw-r--r--core/res/res/values-pa/strings.xml4
-rw-r--r--core/res/res/values-pl/strings.xml4
-rw-r--r--core/res/res/values-pt-rBR/strings.xml4
-rw-r--r--core/res/res/values-pt-rPT/strings.xml4
-rw-r--r--core/res/res/values-pt/strings.xml4
-rw-r--r--core/res/res/values-ro/strings.xml4
-rw-r--r--core/res/res/values-ru/strings.xml4
-rw-r--r--core/res/res/values-si/strings.xml4
-rw-r--r--core/res/res/values-sk/strings.xml4
-rw-r--r--core/res/res/values-sl/strings.xml4
-rw-r--r--core/res/res/values-sq/strings.xml4
-rw-r--r--core/res/res/values-sr/strings.xml4
-rw-r--r--core/res/res/values-sv/strings.xml4
-rw-r--r--core/res/res/values-sw/strings.xml4
-rw-r--r--core/res/res/values-ta/strings.xml4
-rw-r--r--core/res/res/values-te/strings.xml8
-rw-r--r--core/res/res/values-th/strings.xml4
-rw-r--r--core/res/res/values-tl/strings.xml4
-rw-r--r--core/res/res/values-tr/strings.xml4
-rw-r--r--core/res/res/values-uk/strings.xml4
-rw-r--r--core/res/res/values-ur/strings.xml4
-rw-r--r--core/res/res/values-uz/strings.xml4
-rw-r--r--core/res/res/values-vi/strings.xml4
-rw-r--r--core/res/res/values-zh-rCN/strings.xml4
-rw-r--r--core/res/res/values-zh-rHK/strings.xml4
-rw-r--r--core/res/res/values-zh-rTW/strings.xml4
-rw-r--r--core/res/res/values-zu/strings.xml4
-rw-r--r--core/res/res/values/config_telephony.xml6
-rw-r--r--core/res/res/values/ids.xml3
-rw-r--r--core/res/res/values/public-staging.xml1
-rw-r--r--core/res/res/values/strings.xml257
-rw-r--r--core/res/res/values/symbols.xml70
-rw-r--r--core/tests/coretests/src/android/text/EmojiConsistencyTest.java37
-rw-r--r--core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java126
-rw-r--r--data/etc/services.core.protolog.json12
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java1
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java45
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java18
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java3
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java127
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml154
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml28
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp3
-rw-r--r--libs/hwui/jni/Paint.cpp3
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.cpp1
-rw-r--r--media/java/android/media/DeniedByServerException.java7
-rw-r--r--media/java/android/media/MediaCodec.java81
-rw-r--r--media/java/android/media/MediaCryptoException.java31
-rw-r--r--media/java/android/media/MediaDrm.java64
-rw-r--r--media/java/android/media/MediaDrmException.java31
-rw-r--r--media/java/android/media/MediaDrmResetException.java2
-rw-r--r--media/java/android/media/MediaDrmThrowable.java62
-rw-r--r--media/java/android/media/NotProvisionedException.java7
-rw-r--r--media/java/android/media/ResourceBusyException.java7
-rw-r--r--native/android/libandroid.map.txt2
-rw-r--r--native/android/performance_hint.cpp8
-rw-r--r--packages/BackupRestoreConfirmation/res/values-b+sr+Latn/strings.xml38
-rw-r--r--packages/CarrierDefaultApp/res/values-am/strings.xml18
-rw-r--r--packages/CarrierDefaultApp/res/values-ar/strings.xml4
-rw-r--r--packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml36
-rw-r--r--packages/CarrierDefaultApp/res/values-te/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml62
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rAU/strings.xml30
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rCA/strings.xml30
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rGB/strings.xml30
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rIN/strings.xml30
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rXC/strings.xml30
-rw-r--r--packages/CompanionDeviceManager/res/values-th/strings.xml30
-rw-r--r--packages/CompanionDeviceManager/res/values-tl/strings.xml30
-rw-r--r--packages/CredentialManager/res/values-af/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-am/strings.xml33
-rw-r--r--packages/CredentialManager/res/values-b+sr+Latn/strings.xml104
-rw-r--r--packages/CredentialManager/res/values-bn/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-da/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-es-rUS/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-es/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-fr-rCA/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-hu/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-hy/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-in/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-ja/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-ka/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-ko/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-lo/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-lt/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-mk/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-mn/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-mr/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-ms/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-my/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-nb/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-ro/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-ru/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-te/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-th/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-tl/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-tr/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-ur/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-zh-rCN/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-zh-rHK/strings.xml3
-rw-r--r--packages/CredentialManager/res/values-zh-rTW/strings.xml3
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt5
-rw-r--r--packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml26
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml2
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml2
-rw-r--r--packages/SettingsLib/OWNERS3
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-b+sr+Latn/strings.xml2
-rw-r--r--packages/SettingsLib/Spa/build.gradle2
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/MessageFormats.kt2
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerTest.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt7
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt15
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt47
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt32
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt1
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt1
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt104
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt76
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt14
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_le_audio.xml14
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_le_audio_speakers.xml31
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml3
-rw-r--r--packages/SettingsLib/res/values/strings.xml4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java26
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java15
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java70
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java45
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/Shell/res/values-b+sr+Latn/strings.xml54
-rw-r--r--packages/SoundPicker/res/values-b+sr+Latn/strings.xml20
-rw-r--r--packages/SystemUI/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt6
-rw-r--r--packages/SystemUI/res-keyguard/values-am/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml122
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rAU/strings.xml27
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rCA/strings.xml27
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rGB/strings.xml27
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rIN/strings.xml27
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rXC/strings.xml27
-rw-r--r--packages/SystemUI/res-keyguard/values-th/strings.xml27
-rw-r--r--packages/SystemUI/res-keyguard/values-tl/strings.xml27
-rw-r--r--packages/SystemUI/res-keyguard/values/strings.xml2
-rw-r--r--packages/SystemUI/res-product/values-b+sr+Latn/strings.xml48
-rw-r--r--packages/SystemUI/res-product/values-en-rAU/strings.xml6
-rw-r--r--packages/SystemUI/res-product/values-en-rCA/strings.xml6
-rw-r--r--packages/SystemUI/res-product/values-en-rGB/strings.xml6
-rw-r--r--packages/SystemUI/res-product/values-en-rIN/strings.xml6
-rw-r--r--packages/SystemUI/res-product/values-en-rXC/strings.xml6
-rw-r--r--packages/SystemUI/res-product/values-th/strings.xml6
-rw-r--r--packages/SystemUI/res-product/values-tl/strings.xml6
-rw-r--r--packages/SystemUI/res/drawable/ic_do_not_disturb.xml25
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn-land/strings.xml2
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn-ldrtl/strings.xml2
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml26
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml174
-rw-r--r--packages/SystemUI/res/values/flags.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardActiveUnlockModel.kt114
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt162
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt166
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt99
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt73
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java73
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt169
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt190
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt105
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java90
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt198
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt251
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt144
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt140
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt210
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt33
-rw-r--r--packages/VpnDialogs/res/values-b+sr+Latn/strings.xml34
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml2
-rw-r--r--packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-b+sr+Latn/strings.xml2
-rw-r--r--packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-b+sr+Latn/strings.xml2
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml2
-rw-r--r--services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java40
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java52
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java22
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java21
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java61
-rw-r--r--services/core/java/com/android/server/BinaryTransparencyService.java109
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java4
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java30
-rw-r--r--services/core/java/com/android/server/biometrics/AuthSession.java14
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java2
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricContext.java30
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java148
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricContextSessionInfo.java59
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java79
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricLogger.java26
-rw-r--r--services/core/java/com/android/server/biometrics/log/OperationContextExt.java138
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java2
-rw-r--r--services/core/java/com/android/server/cpu/CpuInfoReader.java520
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java57
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java48
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java49
-rw-r--r--services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java127
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessReason.java5
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java2
-rw-r--r--services/core/java/com/android/server/input/InputManagerInternal.java20
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java52
-rw-r--r--services/core/java/com/android/server/input/NativeInputManagerService.java5
-rw-r--r--services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java32
-rw-r--r--services/core/java/com/android/server/pm/BackgroundInstallControlService.java57
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java116
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java17
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/UserTypeDetails.java32
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java6
-rw-r--r--services/core/java/com/android/server/power/OWNERS1
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java23
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java106
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java44
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java140
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java150
-rw-r--r--services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java31
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java13
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java45
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java53
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java9
-rw-r--r--services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java22
-rw-r--r--services/core/java/com/android/server/wm/Task.java14
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java30
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java7
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java26
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java16
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp46
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd14
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt9
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerService.java18
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderCreateSession.java15
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderGetSession.java36
-rw-r--r--services/credentials/java/com/android/server/credentials/RemoteCredentialService.java62
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java162
-rw-r--r--services/permission/java/com/android/server/permission/access/AccessPersistence.kt29
-rw-r--r--services/permission/java/com/android/server/permission/access/AccessPolicy.kt24
-rw-r--r--services/permission/java/com/android/server/permission/access/AccessState.kt10
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/AppOpService.kt380
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/BaseAppOpPersistence.kt8
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt10
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/PackageAppOpPersistence.kt21
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt13
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/UidAppOpPersistence.kt44
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt10
-rw-r--r--services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt7
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/Permission.kt16
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt5
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt377
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt99
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt275
-rw-r--r--services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/affected_cpus2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java604
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java8
-rw-r--r--services/tests/servicestests/res/xml/usertypes_test_profile.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java107
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java137
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java101
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java148
-rw-r--r--services/tests/servicestests/src/com/android/server/display/TestUtils.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt18
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java68
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java75
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java105
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java13
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java35
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java110
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java4
-rwxr-xr-xtelephony/java/android/telephony/ims/ImsCallSession.java49
-rw-r--r--telephony/java/android/telephony/ims/ImsCallSessionListener.java71
-rw-r--r--telephony/java/android/telephony/ims/ImsRegistrationAttributes.java58
-rw-r--r--telephony/java/android/telephony/ims/PublishAttributes.aidl19
-rw-r--r--telephony/java/android/telephony/ims/PublishAttributes.java180
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java68
-rw-r--r--telephony/java/android/telephony/ims/RegistrationManager.java30
-rw-r--r--telephony/java/android/telephony/ims/SipDelegateManager.java65
-rw-r--r--telephony/java/android/telephony/ims/SipDetails.aidl19
-rw-r--r--telephony/java/android/telephony/ims/SipDetails.java290
-rw-r--r--telephony/java/android/telephony/ims/SipDialogState.aidl19
-rw-r--r--telephony/java/android/telephony/ims/SipDialogState.java135
-rw-r--r--telephony/java/android/telephony/ims/SipDialogStateCallback.java103
-rw-r--r--telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java30
-rw-r--r--telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl5
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl13
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl6
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl5
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl2
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsTrafficSessionCallback.aidl27
-rw-r--r--telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl4
-rw-r--r--telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl6
-rw-r--r--telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl3
-rw-r--r--telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl4
-rw-r--r--telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java28
-rw-r--r--telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java27
-rwxr-xr-xtelephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java21
-rw-r--r--telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.aidl19
-rw-r--r--telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java185
-rw-r--r--telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java38
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java375
-rw-r--r--telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java20
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java66
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java63
-rw-r--r--telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java49
-rw-r--r--telephony/java/com/android/ims/internal/IImsCallSession.aidl11
-rw-r--r--telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl13
-rw-r--r--telephony/java/com/android/internal/telephony/ISipDialogStateCallback.aidl27
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl17
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java2
-rw-r--r--tests/Input/src/com/android/test/input/InputDeviceTest.java4
-rw-r--r--tests/SurfaceControlViewHostTest/OWNERS1
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt1
-rw-r--r--tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java3
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java12
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java106
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java8
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java21
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java124
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java380
-rw-r--r--wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java8
578 files changed, 16433 insertions, 6527 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 19ab5bcc9967..deb97a5873ab 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -739,7 +739,6 @@ public class JobInfo implements Parcelable {
/**
* @see JobInfo.Builder#setDataTransfer(boolean)
- * @hide
*/
public boolean isDataTransfer() {
return (flags & FLAG_DATA_TRANSFER) != 0;
@@ -747,7 +746,6 @@ public class JobInfo implements Parcelable {
/**
* @see JobInfo.Builder#setUserInitiated(boolean)
- * @hide
*/
public boolean isUserInitiated() {
return (flags & FLAG_USER_INITIATED) != 0;
@@ -1445,6 +1443,7 @@ public class JobInfo implements Parcelable {
* reasonable estimates should use the sentinel value
* {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
* </ul>
+ * TODO(255371817): update documentation to reflect how this data will be used
* Note that the system may choose to delay jobs with large network
* usage estimates when the device has a poor network connection, in
* order to save battery and possible network costs.
@@ -1852,11 +1851,6 @@ public class JobInfo implements Parcelable {
* being transferred is potentially very large and can take a long time to complete.
*
* <p>
- * The app must hold the {@link android.Manifest.permission#RUN_LONG_JOBS} permission to
- * use this API. JobScheduler will throw a {@link SecurityException} if an app without the
- * permission granted attempts to schedule a data transfer job.
- *
- * <p>
* You must provide an estimate of the payload size via
* {@link #setEstimatedNetworkBytes(long, long)} when scheduling the job or use
* {@link JobService#updateEstimatedNetworkBytes(JobParameters, long, long)} or
@@ -1873,7 +1867,6 @@ public class JobInfo implements Parcelable {
* {@link JobWorkItem JobWorkItems} along with {@link #setDataTransfer(boolean)}.
*
* @see JobInfo#isDataTransfer()
- * @hide
*/
@NonNull
public Builder setDataTransfer(boolean dataTransfer) {
@@ -1903,12 +1896,17 @@ public class JobInfo implements Parcelable {
* shown in the Task Manager when running.
*
* <p>
+ * If the app doesn't hold the {@link android.Manifest.permission#RUN_LONG_JOBS} permission
+ * when scheduling a user-initiated job, JobScheduler will throw a
+ * {@link SecurityException}.
+ *
+ * <p>
* These jobs will not be subject to quotas and will be started immediately once scheduled
* if all constraints are met and the device system health allows for additional tasks.
*
* @see JobInfo#isUserInitiated()
- * @hide
*/
+ @RequiresPermission(android.Manifest.permission.RUN_LONG_JOBS)
@NonNull
public Builder setUserInitiated(boolean userInitiated) {
if (userInitiated) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 1720534e1183..a5a7f93137d6 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -403,7 +403,6 @@ public class JobParameters implements Parcelable {
* {@link android.Manifest.permission#RUN_LONG_JOBS} permission.
*
* @see JobInfo.Builder#setUserInitiated(boolean)
- * @hide
*/
public boolean isUserInitiatedJob() {
return mIsUserInitiated;
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index 898557dfb4f9..449c316c1c65 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -232,16 +232,8 @@ public abstract class JobService extends Service {
public abstract boolean onStopJob(JobParameters params);
/**
- * Update how much data this job will transfer. This method can
- * be called multiple times within the first 30 seconds after
- * {@link #onStartJob(JobParameters)} has been called. Only
- * one call will be heeded after that time has passed.
+ * Update the amount of data this job is estimated to transfer after the job has started.
*
- * This method (or an overload) must be called within the first
- * 30 seconds for a data transfer job if a payload size estimate
- * was not provided at the time of scheduling.
- *
- * @hide
* @see JobInfo.Builder#setEstimatedNetworkBytes(long, long)
*/
public final void updateEstimatedNetworkBytes(@NonNull JobParameters params,
@@ -250,16 +242,9 @@ public abstract class JobService extends Service {
}
/**
- * Update how much data will transfer for the JobWorkItem. This
- * method can be called multiple times within the first 30 seconds
- * after {@link #onStartJob(JobParameters)} has been called.
- * Only one call will be heeded after that time has passed.
+ * Update the amount of data this JobWorkItem is estimated to transfer after the job has
+ * started.
*
- * This method (or an overload) must be called within the first
- * 30 seconds for a data transfer job if a payload size estimate
- * was not provided at the time of scheduling.
- *
- * @hide
* @see JobInfo.Builder#setEstimatedNetworkBytes(long, long)
*/
public final void updateEstimatedNetworkBytes(@NonNull JobParameters params,
@@ -270,7 +255,6 @@ public abstract class JobService extends Service {
/**
* Tell JobScheduler how much data has successfully been transferred for the data transfer job.
- * @hide
*/
public final void updateTransferredNetworkBytes(@NonNull JobParameters params,
@BytesLong long transferredDownloadBytes, @BytesLong long transferredUploadBytes) {
@@ -281,7 +265,6 @@ public abstract class JobService extends Service {
/**
* Tell JobScheduler how much data has been transferred for the data transfer
* {@link JobWorkItem}.
- * @hide
*/
public final void updateTransferredNetworkBytes(@NonNull JobParameters params,
@NonNull JobWorkItem item,
@@ -428,7 +411,6 @@ public abstract class JobService extends Service {
* Notification)}.
* @param notification The notification to be displayed.
* @param jobEndNotificationPolicy The policy to apply to the notification when the job stops.
- * @hide
*/
public final void setNotification(@NonNull JobParameters params, int notificationId,
@NonNull Notification notification,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 651853bae68e..397d2c4ca679 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1114,7 +1114,7 @@ class JobConcurrencyManager {
// reasonably concurrency behavior.
return job.lastEvaluatedBias == JobInfo.BIAS_TOP_APP
// TODO(): include BAL state for user-initiated jobs
- && (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiated());
+ && (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob());
}
@GuardedBy("mLock")
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 e9b966083851..1898e499f7e5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2851,7 +2851,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
final boolean shouldForceBatchJob;
- if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiated()) {
+ if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
// Never batch expedited or user-initiated jobs, even for RESTRICTED apps.
shouldForceBatchJob = false;
} else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
@@ -3176,7 +3176,7 @@ public class JobSchedulerService extends com.android.server.SystemService
synchronized (mLock) {
final boolean shouldTreatAsDataTransfer = job.getJob().isDataTransfer()
&& checkRunLongJobsPermission(job.getSourceUid(), job.getSourcePackageName());
- if (job.shouldTreatAsUserInitiated()) {
+ if (job.shouldTreatAsUserInitiatedJob()) {
if (shouldTreatAsDataTransfer) {
final long estimatedTransferTimeMs =
mConnectivityController.getEstimatedTransferTimeMs(job);
@@ -3215,13 +3215,13 @@ public class JobSchedulerService extends com.android.server.SystemService
synchronized (mLock) {
final boolean allowLongerJob;
final boolean isDataTransfer = job.getJob().isDataTransfer();
- if (isDataTransfer || job.shouldTreatAsUserInitiated()) {
+ if (isDataTransfer || job.shouldTreatAsUserInitiatedJob()) {
allowLongerJob =
checkRunLongJobsPermission(job.getSourceUid(), job.getSourcePackageName());
} else {
allowLongerJob = false;
}
- if (job.shouldTreatAsUserInitiated()) {
+ if (job.shouldTreatAsUserInitiatedJob()) {
if (isDataTransfer && allowLongerJob) {
return mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS;
}
@@ -3885,7 +3885,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final int callingUid = Binder.getCallingUid();
if (callingUid != uid && !UserHandle.isCore(callingUid)) {
throw new SecurityException("Uid " + callingUid
- + " cannot query canRunLongJobs for package " + packageName);
+ + " cannot query hasRunLongJobsPermission for package " + packageName);
}
return checkRunLongJobsPermission(uid, packageName);
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 dae2bb2fb571..15fc3c9a5ec6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -307,7 +307,7 @@ public final class JobServiceContext implements ServiceConnection {
mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
isDeadlineExpired, job.shouldTreatAsExpeditedJob(),
- job.shouldTreatAsUserInitiated(), triggeredUris, triggeredAuthorities,
+ job.shouldTreatAsUserInitiatedJob(), triggeredUris, triggeredAuthorities,
job.network);
mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();
mMinExecutionGuaranteeMillis = mService.getMinJobExecutionGuaranteeMs(job);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 3610b0a0064b..b49129145811 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -427,7 +427,7 @@ public final class ConnectivityController extends RestrictingController implemen
final UidStats uidStats =
getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
- if (jobStatus.shouldTreatAsExpeditedJob() && jobStatus.shouldTreatAsUserInitiated()) {
+ if (jobStatus.shouldTreatAsExpeditedJob() && jobStatus.shouldTreatAsUserInitiatedJob()) {
if (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY)) {
// Don't request a direct hole through any of the firewalls. Instead, mark the
// constraint as satisfied if the network is available, and the job will get
@@ -971,14 +971,14 @@ public final class ConnectivityController extends RestrictingController implemen
if (job.shouldTreatAsExpeditedJob() || job.startedAsExpeditedJob) {
us.earliestEJEnqueueTime =
Math.min(us.earliestEJEnqueueTime, job.enqueueTime);
- } else if (job.shouldTreatAsUserInitiated()) {
+ } else if (job.shouldTreatAsUserInitiatedJob()) {
us.earliestUIJEnqueueTime =
Math.min(us.earliestUIJEnqueueTime, job.enqueueTime);
}
}
if (job.shouldTreatAsExpeditedJob() || job.startedAsExpeditedJob) {
us.numEJs++;
- } else if (job.shouldTreatAsUserInitiated()) {
+ } else if (job.shouldTreatAsUserInitiatedJob()) {
us.numUIJs++;
} else {
us.numRegular++;
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 0b1b7b1733e7..9b6186ea632d 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
@@ -1350,9 +1350,8 @@ public final class JobStatus {
* @return true if the job was scheduled as a user-initiated job and it hasn't been downgraded
* for any reason.
*/
- public boolean shouldTreatAsUserInitiated() {
+ public boolean shouldTreatAsUserInitiatedJob() {
// TODO(248386641): update implementation to handle loss of privilege
- // and also rename to `shouldTreatAsUserInitiatedJob` for consistency
return getJob().isUserInitiated();
}
@@ -1372,7 +1371,7 @@ public final class JobStatus {
* @return true if this is a job whose execution should be made visible to the user.
*/
public boolean isUserVisibleJob() {
- return shouldTreatAsUserInitiated();
+ return shouldTreatAsUserInitiatedJob();
}
/**
@@ -1383,14 +1382,14 @@ public final class JobStatus {
return appHasDozeExemption
|| (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
|| ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
- || shouldTreatAsUserInitiated()
+ || shouldTreatAsUserInitiatedJob()
&& (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
}
boolean canRunInBatterySaver() {
return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
|| ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
- || shouldTreatAsUserInitiated()
+ || shouldTreatAsUserInitiatedJob()
&& (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 404186d27923..cbfad9463013 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -641,7 +641,7 @@ public final class QuotaController extends StateController {
mTopStartedJobs.add(jobStatus);
// Top jobs won't count towards quota so there's no need to involve the Timer.
return;
- } else if (jobStatus.shouldTreatAsUserInitiated()) {
+ } else if (jobStatus.shouldTreatAsUserInitiatedJob()) {
// User-initiated jobs won't count towards quota.
return;
}
@@ -895,7 +895,7 @@ public final class QuotaController extends StateController {
// 1. it was started while the app was in the TOP state
// 2. the app is currently in the foreground
// 3. the app overall is within its quota
- return jobStatus.shouldTreatAsUserInitiated()
+ return jobStatus.shouldTreatAsUserInitiatedJob()
|| isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())
|| isWithinQuotaLocked(
@@ -2120,7 +2120,7 @@ public final class QuotaController extends StateController {
}
void startTrackingJobLocked(@NonNull JobStatus jobStatus) {
- if (jobStatus.shouldTreatAsUserInitiated()) {
+ if (jobStatus.shouldTreatAsUserInitiatedJob()) {
if (DEBUG) {
Slog.v(TAG, "Timer ignoring " + jobStatus.toShortString()
+ " because it's user-initiated");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
index de065b2ffa26..792155b82c1e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
@@ -313,7 +313,7 @@ public class TareController extends StateController {
@GuardedBy("mLock")
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
final long nowElapsed = sElapsedRealtimeClock.millis();
- if (jobStatus.shouldTreatAsUserInitiated()) {
+ if (jobStatus.shouldTreatAsUserInitiatedJob()) {
// User-initiated jobs should always be allowed to run.
jobStatus.setTareWealthConstraintSatisfied(nowElapsed, true);
return;
@@ -331,7 +331,7 @@ public class TareController extends StateController {
@Override
@GuardedBy("mLock")
public void prepareForExecutionLocked(JobStatus jobStatus) {
- if (jobStatus.shouldTreatAsUserInitiated()) {
+ if (jobStatus.shouldTreatAsUserInitiatedJob()) {
// TODO(202954395): consider noting execution with the EconomyManager even though it
// won't affect this job
return;
@@ -365,7 +365,7 @@ public class TareController extends StateController {
@Override
@GuardedBy("mLock")
public void unprepareFromExecutionLocked(JobStatus jobStatus) {
- if (jobStatus.shouldTreatAsUserInitiated()) {
+ if (jobStatus.shouldTreatAsUserInitiatedJob()) {
return;
}
final int userId = jobStatus.getSourceUserId();
@@ -397,7 +397,7 @@ public class TareController extends StateController {
@Override
@GuardedBy("mLock")
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
- if (jobStatus.shouldTreatAsUserInitiated()) {
+ if (jobStatus.shouldTreatAsUserInitiatedJob()) {
return;
}
final int userId = jobStatus.getSourceUserId();
@@ -653,7 +653,7 @@ public class TareController extends StateController {
if (!mIsEnabled) {
return true;
}
- if (jobStatus.shouldTreatAsUserInitiated()) {
+ if (jobStatus.shouldTreatAsUserInitiatedJob()) {
// Always allow user-initiated jobs.
return true;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index 85b762d1e524..eb43c38f76a3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -98,7 +98,7 @@ public class ThermalStatusRestriction extends JobRestriction {
// Only let high priority jobs run if:
// They are already running and aren't yet in overtime
// Don't let any other job run.
- if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiated()) {
+ if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
return job.getNumPreviousAttempts() > 0
|| (mService.isCurrentlyRunningLocked(job)
&& mService.isJobInOvertimeLocked(job));
diff --git a/core/api/current.txt b/core/api/current.txt
index 1ff2a2a6fa27..9ef88c4b963b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2115,6 +2115,7 @@ package android {
field public static final int accessibilityActionPageUp = 16908358; // 0x1020046
field public static final int accessibilityActionPressAndHold = 16908362; // 0x102004a
field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a
+ field public static final int accessibilityActionScrollInDirection;
field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039
field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b
field public static final int accessibilityActionScrollToPosition = 16908343; // 0x1020037
@@ -8388,6 +8389,7 @@ package android.app.job {
method public long getTriggerContentMaxDelay();
method public long getTriggerContentUpdateDelay();
method @Nullable public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
+ method public boolean isDataTransfer();
method public boolean isExpedited();
method public boolean isImportantWhileForeground();
method public boolean isPeriodic();
@@ -8397,6 +8399,7 @@ package android.app.job {
method public boolean isRequireCharging();
method public boolean isRequireDeviceIdle();
method public boolean isRequireStorageNotLow();
+ method public boolean isUserInitiated();
method public void writeToParcel(android.os.Parcel, int);
field public static final int BACKOFF_POLICY_EXPONENTIAL = 1; // 0x1
field public static final int BACKOFF_POLICY_LINEAR = 0; // 0x0
@@ -8423,6 +8426,7 @@ package android.app.job {
method public android.app.job.JobInfo build();
method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
method public android.app.job.JobInfo.Builder setClipData(@Nullable android.content.ClipData, int);
+ method @NonNull public android.app.job.JobInfo.Builder setDataTransfer(boolean);
method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
method @NonNull public android.app.job.JobInfo.Builder setExpedited(boolean);
method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
@@ -8444,6 +8448,7 @@ package android.app.job {
method public android.app.job.JobInfo.Builder setTransientExtras(@NonNull android.os.Bundle);
method public android.app.job.JobInfo.Builder setTriggerContentMaxDelay(long);
method public android.app.job.JobInfo.Builder setTriggerContentUpdateDelay(long);
+ method @NonNull @RequiresPermission(android.Manifest.permission.RUN_LONG_JOBS) public android.app.job.JobInfo.Builder setUserInitiated(boolean);
}
public static final class JobInfo.TriggerContentUri implements android.os.Parcelable {
@@ -8471,6 +8476,7 @@ package android.app.job {
method @Nullable public android.net.Uri[] getTriggeredContentUris();
method public boolean isExpeditedJob();
method public boolean isOverrideDeadlineExpired();
+ method public boolean isUserInitiatedJob();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobParameters> CREATOR;
field public static final int STOP_REASON_APP_STANDBY = 12; // 0xc
@@ -8529,6 +8535,11 @@ package android.app.job {
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract boolean onStartJob(android.app.job.JobParameters);
method public abstract boolean onStopJob(android.app.job.JobParameters);
+ method public final void setNotification(@NonNull android.app.job.JobParameters, int, @NonNull android.app.Notification, int);
+ method public final void updateEstimatedNetworkBytes(@NonNull android.app.job.JobParameters, long, long);
+ method public final void updateEstimatedNetworkBytes(@NonNull android.app.job.JobParameters, @NonNull android.app.job.JobWorkItem, long, long);
+ method public final void updateTransferredNetworkBytes(@NonNull android.app.job.JobParameters, long, long);
+ method public final void updateTransferredNetworkBytes(@NonNull android.app.job.JobParameters, @NonNull android.app.job.JobWorkItem, long, long);
field public static final String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE";
}
@@ -11809,6 +11820,7 @@ package android.content.pm {
method public void close();
method public void commit(@NonNull android.content.IntentSender);
method public void fsync(@NonNull java.io.OutputStream) throws java.io.IOException;
+ method @NonNull public android.os.PersistableBundle getAppMetadata();
method @NonNull public int[] getChildSessionIds();
method @NonNull public String[] getNames() throws java.io.IOException;
method public int getParentSessionId();
@@ -11821,6 +11833,7 @@ package android.content.pm {
method public void removeSplit(@NonNull String) throws java.io.IOException;
method public void requestChecksums(@NonNull String, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull java.util.concurrent.Executor, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, java.io.FileNotFoundException;
method public void requestUserPreapproval(@NonNull android.content.pm.PackageInstaller.PreapprovalDetails, @NonNull android.content.IntentSender);
+ method public void setAppMetadata(@Nullable android.os.PersistableBundle) throws java.io.IOException;
method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException;
method public void setStagingProgress(float);
method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -21800,6 +21813,7 @@ package android.media {
method public void subscribeToVendorParameters(@NonNull java.util.List<java.lang.String>);
method public void unsubscribeFromVendorParameters(@NonNull java.util.List<java.lang.String>);
field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2
+ field public static final int BUFFER_FLAG_DECODE_ONLY = 32; // 0x20
field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4
field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
field public static final int BUFFER_FLAG_PARTIAL_FRAME = 8; // 0x8
@@ -21850,7 +21864,7 @@ package android.media {
field public static final int ERROR_RECLAIMED = 1101; // 0x44d
}
- public static final class MediaCodec.CryptoException extends java.lang.RuntimeException {
+ public static final class MediaCodec.CryptoException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaCodec.CryptoException(int, @Nullable String);
method public int getErrorCode();
field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
@@ -21887,6 +21901,9 @@ package android.media {
public class MediaCodec.IncompatibleWithBlockModelException extends java.lang.RuntimeException {
}
+ public class MediaCodec.InvalidBufferFlagsException extends java.lang.RuntimeException {
+ }
+
public static final class MediaCodec.LinearBlock {
method protected void finalize();
method public static boolean isCodecCopyFreeCompatible(@NonNull String[]);
@@ -22350,7 +22367,7 @@ package android.media {
method public void setMediaDrmSession(@NonNull byte[]) throws android.media.MediaCryptoException;
}
- public final class MediaCryptoException extends java.lang.Exception {
+ public final class MediaCryptoException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaCryptoException(@Nullable String);
}
@@ -22573,7 +22590,7 @@ package android.media {
method public long getTimestampMillis();
}
- public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
+ public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
method @NonNull public String getDiagnosticInfo();
method public int getErrorCode();
method public boolean isTransient();
@@ -22646,7 +22663,7 @@ package android.media {
@Deprecated @IntDef({android.media.MediaDrm.SECURITY_LEVEL_UNKNOWN, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.SecurityLevel {
}
- public static final class MediaDrm.SessionException extends java.lang.RuntimeException {
+ public static final class MediaDrm.SessionException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaDrm.SessionException(int, @Nullable String);
method @Deprecated public int getErrorCode();
method public boolean isTransient();
@@ -22654,14 +22671,20 @@ package android.media {
field @Deprecated public static final int ERROR_UNKNOWN = 0; // 0x0
}
- public class MediaDrmException extends java.lang.Exception {
+ public class MediaDrmException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaDrmException(String);
}
- public class MediaDrmResetException extends java.lang.IllegalStateException {
+ public class MediaDrmResetException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
ctor public MediaDrmResetException(String);
}
+ public interface MediaDrmThrowable {
+ method public default int getErrorContext();
+ method public default int getOemError();
+ method public default int getVendorError();
+ }
+
public final class MediaExtractor {
ctor public MediaExtractor();
method public boolean advance();
@@ -32481,12 +32504,7 @@ package android.os {
public static class PerformanceHintManager.Session implements java.io.Closeable {
method public void close();
method public void reportActualWorkDuration(long);
- method public void sendHint(int);
method public void updateTargetWorkDuration(long);
- field public static final int CPU_LOAD_DOWN = 1; // 0x1
- field public static final int CPU_LOAD_RESET = 2; // 0x2
- field public static final int CPU_LOAD_RESUME = 3; // 0x3
- field public static final int CPU_LOAD_UP = 0; // 0x0
}
public final class PersistableBundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -39512,9 +39530,9 @@ package android.service.credentials {
}
public final class BeginCreateCredentialRequest implements android.os.Parcelable {
- ctor public BeginCreateCredentialRequest(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
+ ctor public BeginCreateCredentialRequest(@NonNull android.service.credentials.CallingAppInfo, @NonNull String, @NonNull android.os.Bundle);
method public int describeContents();
- method @NonNull public String getCallingPackage();
+ method @NonNull public android.service.credentials.CallingAppInfo getCallingAppInfo();
method @NonNull public android.os.Bundle getData();
method @NonNull public String getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -39546,35 +39564,44 @@ package android.service.credentials {
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.BeginGetCredentialOption> CREATOR;
}
- public final class BeginGetCredentialsRequest implements android.os.Parcelable {
+ public final class BeginGetCredentialRequest implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.service.credentials.BeginGetCredentialOption> getBeginGetCredentialOptions();
- method @NonNull public String getCallingPackage();
+ method @NonNull public android.service.credentials.CallingAppInfo getCallingAppInfo();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.BeginGetCredentialsRequest> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.BeginGetCredentialRequest> CREATOR;
}
- public static final class BeginGetCredentialsRequest.Builder {
- ctor public BeginGetCredentialsRequest.Builder(@NonNull String);
- method @NonNull public android.service.credentials.BeginGetCredentialsRequest.Builder addBeginGetCredentialOption(@NonNull android.service.credentials.BeginGetCredentialOption);
- method @NonNull public android.service.credentials.BeginGetCredentialsRequest build();
- method @NonNull public android.service.credentials.BeginGetCredentialsRequest.Builder setBeginGetCredentialOptions(@NonNull java.util.List<android.service.credentials.BeginGetCredentialOption>);
+ public static final class BeginGetCredentialRequest.Builder {
+ ctor public BeginGetCredentialRequest.Builder(@NonNull android.service.credentials.CallingAppInfo);
+ method @NonNull public android.service.credentials.BeginGetCredentialRequest.Builder addBeginGetCredentialOption(@NonNull android.service.credentials.BeginGetCredentialOption);
+ method @NonNull public android.service.credentials.BeginGetCredentialRequest build();
+ method @NonNull public android.service.credentials.BeginGetCredentialRequest.Builder setBeginGetCredentialOptions(@NonNull java.util.List<android.service.credentials.BeginGetCredentialOption>);
}
- public final class BeginGetCredentialsResponse implements android.os.Parcelable {
- method @NonNull public static android.service.credentials.BeginGetCredentialsResponse createWithAuthentication(@NonNull android.service.credentials.Action);
- method @NonNull public static android.service.credentials.BeginGetCredentialsResponse createWithResponseContent(@NonNull android.service.credentials.CredentialsResponseContent);
+ public final class BeginGetCredentialResponse implements android.os.Parcelable {
+ method @NonNull public static android.service.credentials.BeginGetCredentialResponse createWithAuthentication(@NonNull android.service.credentials.Action);
+ method @NonNull public static android.service.credentials.BeginGetCredentialResponse createWithResponseContent(@NonNull android.service.credentials.CredentialsResponseContent);
method public int describeContents();
method @Nullable public android.service.credentials.Action getAuthenticationAction();
method @Nullable public android.service.credentials.CredentialsResponseContent getCredentialsResponseContent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.BeginGetCredentialsResponse> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.BeginGetCredentialResponse> CREATOR;
+ }
+
+ public final class CallingAppInfo implements android.os.Parcelable {
+ ctor public CallingAppInfo(@NonNull String, @NonNull java.util.Set<android.content.pm.Signature>);
+ method public int describeContents();
+ method @NonNull public String getPackageName();
+ method @NonNull public java.util.Set<android.content.pm.Signature> getSignatures();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.CallingAppInfo> CREATOR;
}
public final class CreateCredentialRequest implements android.os.Parcelable {
- ctor public CreateCredentialRequest(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
+ ctor public CreateCredentialRequest(@NonNull android.service.credentials.CallingAppInfo, @NonNull String, @NonNull android.os.Bundle);
method public int describeContents();
- method @NonNull public String getCallingPackage();
+ method @NonNull public android.service.credentials.CallingAppInfo getCallingAppInfo();
method @NonNull public android.os.Bundle getData();
method @NonNull public String getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -39606,19 +39633,10 @@ package android.service.credentials {
method @NonNull public android.service.credentials.CredentialEntry.Builder setAutoSelectAllowed(@NonNull boolean);
}
- public class CredentialProviderException extends java.lang.Exception {
- ctor public CredentialProviderException(int, @NonNull String, @NonNull Throwable);
- ctor public CredentialProviderException(int, @NonNull String);
- ctor public CredentialProviderException(int, @NonNull Throwable);
- ctor public CredentialProviderException(int);
- method public int getErrorCode();
- field public static final int ERROR_UNKNOWN = 0; // 0x0
- }
-
public abstract class CredentialProviderService extends android.app.Service {
ctor public CredentialProviderService();
- method public abstract void onBeginCreateCredential(@NonNull android.service.credentials.BeginCreateCredentialRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.service.credentials.CredentialProviderException>);
- method public abstract void onBeginGetCredentials(@NonNull android.service.credentials.BeginGetCredentialsRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialsResponse,android.service.credentials.CredentialProviderException>);
+ method public abstract void onBeginCreateCredential(@NonNull android.service.credentials.BeginCreateCredentialRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.credentials.CreateCredentialException>);
+ method public abstract void onBeginGetCredential(@NonNull android.service.credentials.BeginGetCredentialRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException>);
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
field public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
field public static final String EXTRA_CREATE_CREDENTIAL_EXCEPTION = "android.service.credentials.extra.CREATE_CREDENTIAL_EXCEPTION";
@@ -39652,14 +39670,14 @@ package android.service.credentials {
public final class GetCredentialRequest implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public String getCallingPackage();
+ method @NonNull public android.service.credentials.CallingAppInfo getCallingAppInfo();
method @NonNull public java.util.List<android.credentials.GetCredentialOption> getGetCredentialOptions();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.GetCredentialRequest> CREATOR;
}
public static final class GetCredentialRequest.Builder {
- ctor public GetCredentialRequest.Builder(@NonNull String);
+ ctor public GetCredentialRequest.Builder(@NonNull android.service.credentials.CallingAppInfo);
method @NonNull public android.service.credentials.GetCredentialRequest.Builder addGetCredentialOption(@NonNull android.credentials.GetCredentialOption);
method @NonNull public android.service.credentials.GetCredentialRequest build();
method @NonNull public android.service.credentials.GetCredentialRequest.Builder setGetCredentialOptions(@NonNull java.util.List<android.credentials.GetCredentialOption>);
@@ -44344,6 +44362,7 @@ package android.telephony {
method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
+ method @NonNull public String getPrimaryImei();
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState(int);
method @Nullable public android.telephony.SignalStrength getSignalStrength();
@@ -45391,6 +45410,7 @@ package android.telephony.ims {
method public int describeContents();
method public int getAttributeFlags();
method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
+ method @Nullable public android.telephony.ims.SipDetails getSipDetails();
method public int getTransportType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1
@@ -45451,6 +45471,22 @@ package android.telephony.ims {
method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo);
}
+ public final class SipDetails implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCSeq();
+ method @Nullable public String getCallId();
+ method public int getMethod();
+ method public int getReasonHeaderCause();
+ method @NonNull public String getReasonHeaderText();
+ method public int getResponseCode();
+ method @NonNull public String getResponsePhrase();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDetails> CREATOR;
+ field public static final int METHOD_PUBLISH = 2; // 0x2
+ field public static final int METHOD_REGISTER = 1; // 0x1
+ field public static final int METHOD_SUBSCRIBE = 3; // 0x3
+ }
+
}
package android.telephony.ims.feature {
@@ -45828,6 +45864,10 @@ package android.text {
method public android.text.Editable newEditable(CharSequence);
}
+ public final class EmojiConsistency {
+ method @NonNull public static java.util.Set<int[]> getEmojiConsistencySet();
+ }
+
public interface GetChars extends java.lang.CharSequence {
method public void getChars(int, int, char[], int);
}
@@ -52761,6 +52801,7 @@ package android.view.accessibility {
field public static final int TYPE_VIEW_LONG_CLICKED = 2; // 0x2
field public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
field public static final int TYPE_VIEW_SELECTED = 4; // 0x4
+ field public static final int TYPE_VIEW_TARGETED_BY_SCROLL = 67108864; // 0x4000000
field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10
field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
@@ -53001,6 +53042,7 @@ package android.view.accessibility {
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
field public static final String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+ field public static final String ACTION_ARGUMENT_DIRECTION_INT = "android.view.accessibility.action.ARGUMENT_DIRECTION_INT";
field public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
@@ -53093,6 +53135,7 @@ package android.view.accessibility {
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_BACKWARD;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_DOWN;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_FORWARD;
+ field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_IN_DIRECTION;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_LEFT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_RIGHT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_TO_POSITION;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a97da8805373..4ae3176b1a3c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -804,9 +804,9 @@ package android.app {
method public static android.app.BroadcastOptions makeBasic();
method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
- method public void setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter);
- method public void setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
- method public void setDeliveryGroupPolicy(int);
+ method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter);
+ method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
+ method @NonNull public android.app.BroadcastOptions setDeliveryGroupPolicy(int);
method public void setDontSendToRestrictedApps(boolean);
method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
method public void setRequireAllOfPermissions(@Nullable String[]);
@@ -2991,6 +2991,10 @@ package android.companion.virtual {
method public void onTopActivityChanged(int, @NonNull android.content.ComponentName);
}
+ public static interface VirtualDeviceManager.IntentInterceptorCallback {
+ method public void onIntentIntercepted(@NonNull android.content.Intent);
+ }
+
public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
@@ -3009,8 +3013,10 @@ package android.companion.virtual {
method public int getDeviceId();
method @Nullable public android.companion.virtual.sensor.VirtualSensor getVirtualSensor(int, @NonNull String);
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull java.util.concurrent.Executor, @NonNull android.content.IntentFilter, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
}
public final class VirtualDeviceParams implements android.os.Parcelable {
@@ -3614,6 +3620,7 @@ package android.content.pm {
method public abstract boolean arePermissionsIndividuallyControlled();
method @NonNull public boolean[] canPackageQuery(@NonNull String, @NonNull String[]) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(@NonNull String);
+ method @NonNull @RequiresPermission("android.permission.GET_APP_METADATA") public android.os.PersistableBundle getAppMetadata(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.ApplicationInfoFlags, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.dex.ArtManager getArtManager();
@@ -4709,13 +4716,19 @@ package android.hardware.input {
public final class VirtualKeyboardConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public String getLanguageTag();
+ method @NonNull public String getLayoutType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualKeyboardConfig> CREATOR;
+ field public static final String DEFAULT_LANGUAGE_TAG = "en-Latn-US";
+ field public static final String DEFAULT_LAYOUT_TYPE = "qwerty";
}
public static final class VirtualKeyboardConfig.Builder extends android.hardware.input.VirtualInputDeviceConfig.Builder<android.hardware.input.VirtualKeyboardConfig.Builder> {
ctor public VirtualKeyboardConfig.Builder();
method @NonNull public android.hardware.input.VirtualKeyboardConfig build();
+ method @NonNull public android.hardware.input.VirtualKeyboardConfig.Builder setLanguageTag(@NonNull String);
+ method @NonNull public android.hardware.input.VirtualKeyboardConfig.Builder setLayoutType(@NonNull String);
}
public class VirtualMouse implements java.io.Closeable {
@@ -9478,7 +9491,7 @@ package android.net.wifi.nl80211 {
method public void setOnServiceDeadCallback(@NonNull Runnable);
method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback);
method public boolean setupInterfaceForSoftApMode(@NonNull String);
- method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String);
+ method @Deprecated @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String);
method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.nl80211.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.PnoScanRequestCallback);
method @Deprecated public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>);
method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>, @Nullable android.os.Bundle);
@@ -9524,11 +9537,11 @@ package android.net.wifi.nl80211 {
method public void onFailure(int);
}
- public static class WifiNl80211Manager.SignalPollResult {
- field public final int associationFrequencyMHz;
- field public final int currentRssiDbm;
- field public final int rxBitrateMbps;
- field public final int txBitrateMbps;
+ @Deprecated public static class WifiNl80211Manager.SignalPollResult {
+ field @Deprecated public final int associationFrequencyMHz;
+ field @Deprecated public final int currentRssiDbm;
+ field @Deprecated public final int rxBitrateMbps;
+ field @Deprecated public final int txBitrateMbps;
}
@Deprecated public static interface WifiNl80211Manager.SoftApCallback {
@@ -12319,6 +12332,7 @@ package android.service.voice {
method public int describeContents();
method @NonNull public android.media.AudioFormat getAudioFormat();
method @NonNull public android.os.ParcelFileDescriptor getAudioStreamParcelFileDescriptor();
+ method @NonNull public byte[] getInitialAudio();
method @NonNull public android.os.PersistableBundle getMetadata();
method @Nullable public android.media.AudioTimestamp getTimestamp();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -12331,6 +12345,7 @@ package android.service.voice {
method @NonNull public android.service.voice.HotwordAudioStream build();
method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioFormat(@NonNull android.media.AudioFormat);
method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioStreamParcelFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
+ method @NonNull public android.service.voice.HotwordAudioStream.Builder setInitialAudio(@NonNull byte[]);
method @NonNull public android.service.voice.HotwordAudioStream.Builder setMetadata(@NonNull android.os.PersistableBundle);
method @NonNull public android.service.voice.HotwordAudioStream.Builder setTimestamp(@NonNull android.media.AudioTimestamp);
}
@@ -15106,6 +15121,7 @@ package android.telephony.ims {
ctor public ImsRegistrationAttributes.Builder(int);
method @NonNull public android.telephony.ims.ImsRegistrationAttributes build();
method @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setFeatureTags(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setSipDetails(@NonNull android.telephony.ims.SipDetails);
}
public class ImsService extends android.app.Service {
@@ -15392,6 +15408,15 @@ package android.telephony.ims {
method public void onRemoved();
}
+ public final class PublishAttributes implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.telephony.ims.RcsContactPresenceTuple> getPresenceTuples();
+ method public int getPublishState();
+ method @Nullable public android.telephony.ims.SipDetails getSipDetails();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.PublishAttributes> CREATOR;
+ }
+
public final class RcsClientConfiguration implements android.os.Parcelable {
ctor @Deprecated public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
ctor public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String, boolean);
@@ -15549,12 +15574,15 @@ package android.telephony.ims {
public static interface RcsUceAdapter.CapabilitiesCallback {
method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
- method public void onComplete();
- method public void onError(int, long);
+ method @Deprecated public void onComplete();
+ method public default void onComplete(@Nullable android.telephony.ims.SipDetails);
+ method @Deprecated public void onError(int, long);
+ method public default void onError(int, long, @Nullable android.telephony.ims.SipDetails);
}
public static interface RcsUceAdapter.OnPublishStateChangedListener {
- method public void onPublishStateChange(int);
+ method @Deprecated public void onPublishStateChange(int);
+ method public default void onPublishStateChange(@NonNull android.telephony.ims.PublishAttributes);
}
public interface RegistrationManager {
@@ -15563,6 +15591,10 @@ package android.telephony.ims {
field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT = 2; // 0x2
}
+ public static class RegistrationManager.RegistrationCallback {
+ method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo, @NonNull android.telephony.ims.SipDetails);
+ }
+
public final class RtpHeaderExtension implements android.os.Parcelable {
ctor public RtpHeaderExtension(@IntRange(from=1, to=14) int, @NonNull byte[]);
method public int describeContents();
@@ -15663,8 +15695,10 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerSipDialogStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.SipDialogStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String);
method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterSipDialogStateCallback(@NonNull android.telephony.ims.SipDialogStateCallback) throws android.telephony.ims.ImsException;
field public static final int DENIED_REASON_INVALID = 4; // 0x4
field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1
field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2
@@ -15689,6 +15723,22 @@ package android.telephony.ims {
field public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3; // 0x3
}
+ public final class SipDialogState implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getState();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDialogState> CREATOR;
+ field public static final int STATE_CLOSED = 2; // 0x2
+ field public static final int STATE_CONFIRMED = 1; // 0x1
+ field public static final int STATE_EARLY = 0; // 0x0
+ }
+
+ public abstract class SipDialogStateCallback {
+ ctor public SipDialogStateCallback();
+ method public abstract void onActiveSipDialogsChanged(@NonNull java.util.List<android.telephony.ims.SipDialogState>);
+ method public abstract void onError();
+ }
+
public final class SipMessage implements android.os.Parcelable {
ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]);
method public int describeContents();
@@ -15830,7 +15880,8 @@ package android.telephony.ims.feature {
package android.telephony.ims.stub {
public interface CapabilityExchangeEventListener {
- method public default void onPublishUpdated(int, @NonNull String, int, @NonNull String) throws android.telephony.ims.ImsException;
+ method @Deprecated public default void onPublishUpdated(int, @NonNull String, int, @NonNull String) throws android.telephony.ims.ImsException;
+ method public default void onPublishUpdated(@NonNull android.telephony.ims.SipDetails) throws android.telephony.ims.ImsException;
method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException;
method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException;
method public void onUnpublish() throws android.telephony.ims.ImsException;
@@ -15964,6 +16015,8 @@ package android.telephony.ims.stub {
ctor public ImsRegistrationImplBase(@NonNull java.util.concurrent.Executor);
method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
method public final void onDeregistered(@Nullable android.telephony.ims.ImsReasonInfo, int, int);
+ method public final void onDeregistered(@Nullable android.telephony.ims.ImsReasonInfo, @NonNull android.telephony.ims.SipDetails);
+ method public final void onDeregistered(@Nullable android.telephony.ims.ImsReasonInfo, int, int, @NonNull android.telephony.ims.SipDetails);
method public final void onRegistered(int);
method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
method public final void onRegistering(int);
@@ -16051,14 +16104,16 @@ package android.telephony.ims.stub {
public static interface RcsCapabilityExchangeImplBase.PublishResponseCallback {
method public void onCommandError(int) throws android.telephony.ims.ImsException;
- method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
- method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method @Deprecated public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method @Deprecated public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method public default void onNetworkResponse(@NonNull android.telephony.ims.SipDetails) throws android.telephony.ims.ImsException;
}
public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback {
method public void onCommandError(int) throws android.telephony.ims.ImsException;
- method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
- method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method @Deprecated public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method @Deprecated public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method public default void onNetworkResponse(@NonNull android.telephony.ims.SipDetails) throws android.telephony.ims.ImsException;
method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException;
method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException;
method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 28b370680df4..64e2760110e0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1860,6 +1860,14 @@ package android.os {
method public static java.io.File getFile(java.io.FileDescriptor) throws java.io.IOException;
}
+ public static class PerformanceHintManager.Session implements java.io.Closeable {
+ method public void sendHint(int);
+ field public static final int CPU_LOAD_DOWN = 1; // 0x1
+ field public static final int CPU_LOAD_RESET = 2; // 0x2
+ field public static final int CPU_LOAD_RESUME = 3; // 0x3
+ field public static final int CPU_LOAD_UP = 0; // 0x0
+ }
+
public final class PowerManager {
method public boolean areAutoPowerSaveModesEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void forceLowPowerStandbyActive(boolean);
@@ -2684,6 +2692,7 @@ package android.telephony {
method @NonNull public android.util.Pair<java.lang.Integer,java.lang.Integer> getHalVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
method @Deprecated public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDomainSelectionSupported();
method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index a0b3dc0452ac..9cea5e8ef4cf 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -299,6 +299,12 @@ public class ApplicationErrorReport implements Parcelable {
*/
public static class CrashInfo {
/**
+ * The name of the exception handler that is installed.
+ * @hide
+ */
+ public String exceptionHandlerClassName;
+
+ /**
* Class name of the exception that caused the crash.
*/
public String exceptionClassName;
@@ -369,6 +375,14 @@ public class ApplicationErrorReport implements Parcelable {
}
}
+ // Populate the currently installed exception handler.
+ Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
+ if (handler != null) {
+ exceptionHandlerClassName = handler.getClass().getName();
+ } else {
+ exceptionHandlerClassName = "unknown";
+ }
+
exceptionClassName = rootTr.getClass().getName();
if (rootTr.getStackTrace().length > 0) {
StackTraceElement trace = rootTr.getStackTrace()[0];
@@ -416,6 +430,7 @@ public class ApplicationErrorReport implements Parcelable {
* Create an instance of CrashInfo initialized from a Parcel.
*/
public CrashInfo(Parcel in) {
+ exceptionHandlerClassName = in.readString();
exceptionClassName = in.readString();
exceptionMessage = in.readString();
throwFileName = in.readString();
@@ -431,6 +446,7 @@ public class ApplicationErrorReport implements Parcelable {
*/
public void writeToParcel(Parcel dest, int flags) {
int start = dest.dataPosition();
+ dest.writeString(exceptionHandlerClassName);
dest.writeString(exceptionClassName);
dest.writeString(exceptionMessage);
dest.writeString(throwFileName);
@@ -441,6 +457,7 @@ public class ApplicationErrorReport implements Parcelable {
dest.writeString(crashTag);
int total = dest.dataPosition()-start;
if (Binder.CHECK_PARCEL_SIZE && total > 20*1024) {
+ Slog.d("Error", "ERR: exHandler=" + exceptionHandlerClassName);
Slog.d("Error", "ERR: exClass=" + exceptionClassName);
Slog.d("Error", "ERR: exMsg=" + exceptionMessage);
Slog.d("Error", "ERR: file=" + throwFileName);
@@ -455,6 +472,7 @@ public class ApplicationErrorReport implements Parcelable {
* Dump a CrashInfo instance to a Printer.
*/
public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "exceptionHandlerClassName: " + exceptionHandlerClassName);
pw.println(prefix + "exceptionClassName: " + exceptionClassName);
pw.println(prefix + "exceptionMessage: " + exceptionMessage);
pw.println(prefix + "throwFileName: " + throwFileName);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 4d3f9e457ae3..309b2535798e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -126,6 +126,11 @@ import dalvik.system.VMRuntime;
import libcore.util.EmptyArray;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
@@ -1223,6 +1228,33 @@ public class ApplicationPackageManager extends PackageManager {
}
}
+ @Override
+ @NonNull
+ public PersistableBundle getAppMetadata(@NonNull String packageName)
+ throws NameNotFoundException {
+ PersistableBundle appMetadata = null;
+ String path = null;
+ try {
+ path = mPM.getAppMetadataPath(packageName, getUserId());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(NameNotFoundException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (path != null) {
+ File file = new File(path);
+ try (InputStream inputStream = new FileInputStream(file)) {
+ appMetadata = PersistableBundle.readFromStream(inputStream);
+ } catch (FileNotFoundException e) {
+ // ignore and return empty bundle if app metadata does not exist
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return appMetadata != null ? appMetadata : new PersistableBundle();
+ }
+
@SuppressWarnings("unchecked")
@Override
public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions, int flags) {
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 9d5c01af1a02..38855c05535b 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -725,8 +725,10 @@ public class BroadcastOptions extends ComponentOptions {
* @hide
*/
@SystemApi
- public void setDeliveryGroupPolicy(@DeliveryGroupPolicy int policy) {
+ @NonNull
+ public BroadcastOptions setDeliveryGroupPolicy(@DeliveryGroupPolicy int policy) {
mDeliveryGroupPolicy = policy;
+ return this;
}
/**
@@ -766,12 +768,15 @@ public class BroadcastOptions extends ComponentOptions {
* @hide
*/
@SystemApi
- public void setDeliveryGroupMatchingKey(@NonNull String namespace, @NonNull String key) {
+ @NonNull
+ public BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String namespace,
+ @NonNull String key) {
Preconditions.checkArgument(!namespace.contains(":"),
"namespace should not contain ':'");
Preconditions.checkArgument(!key.contains(":"),
"key should not contain ':'");
mDeliveryGroupMatchingKey = namespace + ":" + key;
+ return this;
}
/**
@@ -813,8 +818,10 @@ public class BroadcastOptions extends ComponentOptions {
* @hide
*/
@SystemApi
- public void setDeliveryGroupMatchingFilter(@NonNull IntentFilter matchingFilter) {
+ @NonNull
+ public BroadcastOptions setDeliveryGroupMatchingFilter(@NonNull IntentFilter matchingFilter) {
mDeliveryGroupMatchingFilter = Objects.requireNonNull(matchingFilter);
+ return this;
}
/**
@@ -851,8 +858,10 @@ public class BroadcastOptions extends ComponentOptions {
*
* @hide
*/
- public void setDeliveryGroupExtrasMerger(@NonNull BundleMerger extrasMerger) {
+ @NonNull
+ public BroadcastOptions setDeliveryGroupExtrasMerger(@NonNull BundleMerger extrasMerger) {
mDeliveryGroupExtrasMerger = Objects.requireNonNull(extrasMerger);
+ return this;
}
/**
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index db47a4cdefc0..9c33729cf9fe 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -33,6 +33,8 @@ import android.app.ActivityManager.PendingIntentInfo;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -53,6 +55,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Pair;
import android.util.proto.ProtoOutputStream;
@@ -167,6 +170,12 @@ public final class PendingIntent implements Parcelable {
static final long PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED = 160794467L;
/** @hide */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Overridable
+ public static final long BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT = 236704164L;
+
+ /** @hide */
@IntDef(flag = true,
value = {
FLAG_ONE_SHOT,
@@ -381,17 +390,19 @@ public final class PendingIntent implements Parcelable {
sOnMarshaledListener.set(listener);
}
- private static void checkFlags(int flags, String packageName) {
- final boolean flagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0;
- final boolean flagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
+ private static void checkPendingIntent(int flags, @NonNull Intent intent,
+ @NonNull Context context) {
+ final boolean isFlagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0;
+ final boolean isFlagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
+ final String packageName = context.getPackageName();
- if (flagImmutableSet && flagMutableSet) {
+ if (isFlagImmutableSet && isFlagMutableSet) {
throw new IllegalArgumentException(
"Cannot set both FLAG_IMMUTABLE and FLAG_MUTABLE for PendingIntent");
}
if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
- && !flagImmutableSet && !flagMutableSet) {
+ && !isFlagImmutableSet && !isFlagMutableSet) {
String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S
+ " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE"
+ " be specified when creating a PendingIntent.\nStrongly consider"
@@ -400,6 +411,40 @@ public final class PendingIntent implements Parcelable {
+ " be used with inline replies or bubbles.";
throw new IllegalArgumentException(msg);
}
+
+ // Whenever creation or retrieval of a mutable implicit PendingIntent occurs:
+ // - For apps with target SDK >= U, Log.wtfStack() that it is blocked for security reasons.
+ // This will be changed to a throw of an exception on the server side once we finish
+ // migrating to safer PendingIntents b/262253127.
+ // - Otherwise, warn that it will be blocked from target SDK U.
+ if (isNewMutableImplicitPendingIntent(flags, intent)) {
+ if (Compatibility.isChangeEnabled(BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT)) {
+ String msg = packageName + ": Targeting U+ (version "
+ + Build.VERSION_CODES.UPSIDE_DOWN_CAKE + " and above) disallows"
+ + " creating or retrieving a PendingIntent with FLAG_MUTABLE,"
+ + " an implicit Intent within and without FLAG_NO_CREATE for"
+ + " security reasons. To retrieve an already existing"
+ + " PendingIntent, use FLAG_NO_CREATE, however, to create a"
+ + " new PendingIntent with an implicit Intent use"
+ + " FLAG_IMMUTABLE.";
+ Log.wtfStack(TAG, msg);
+ } else {
+ String msg = "New mutable implicit PendingIntent: pkg=" + packageName
+ + ", action=" + intent.getAction()
+ + ", featureId=" + context.getAttributionTag()
+ + ". This will be blocked once the app targets U+"
+ + " for security reasons.";
+ Log.w(TAG, new RuntimeException(msg));
+ }
+ }
+ }
+
+ /** @hide */
+ public static boolean isNewMutableImplicitPendingIntent(int flags, @NonNull Intent intent) {
+ boolean isFlagNoCreateSet = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
+ boolean isFlagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
+ boolean isImplicit = (intent.getComponent() == null) && (intent.getPackage() == null);
+ return !isFlagNoCreateSet && isFlagMutableSet && isImplicit;
}
/**
@@ -481,7 +526,7 @@ public final class PendingIntent implements Parcelable {
@NonNull Intent intent, int flags, Bundle options, UserHandle user) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
- checkFlags(flags, packageName);
+ checkPendingIntent(flags, intent, context);
try {
intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
@@ -615,8 +660,8 @@ public final class PendingIntent implements Parcelable {
intents[i].migrateExtraStreamToClipData(context);
intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
+ checkPendingIntent(flags, intents[i], context);
}
- checkFlags(flags, packageName);
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
@@ -668,7 +713,7 @@ public final class PendingIntent implements Parcelable {
Intent intent, int flags, UserHandle userHandle) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
- checkFlags(flags, packageName);
+ checkPendingIntent(flags, intent, context);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
@@ -747,7 +792,7 @@ public final class PendingIntent implements Parcelable {
Intent intent, int flags, int serviceKind) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
- checkFlags(flags, packageName);
+ checkPendingIntent(flags, intent, context);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 3730a6f9b428..df13a8788103 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1045,9 +1045,10 @@ public final class SystemServiceRegistry {
registerService(Context.APPWIDGET_SERVICE, AppWidgetManager.class,
new CachedServiceFetcher<AppWidgetManager>() {
@Override
- public AppWidgetManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.APPWIDGET_SERVICE);
- return new AppWidgetManager(ctx, IAppWidgetService.Stub.asInterface(b));
+ public AppWidgetManager createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
+ return b == null ? null : new AppWidgetManager(ctx,
+ IAppWidgetService.Stub.asInterface(b));
}});
registerService(Context.MIDI_SERVICE, MidiManager.class,
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 814f38b2f3e6..e75b503d5f69 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -76,7 +76,6 @@ import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeoutException;
@@ -484,7 +483,7 @@ public final class UiAutomation {
// Calling out without a lock held.
mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), null);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error executing adopting shell permission identity!", re);
+ throw re.rethrowFromSystemServer();
}
}
@@ -509,7 +508,7 @@ public final class UiAutomation {
// Calling out without a lock held.
mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), permissions);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error executing adopting shell permission identity!", re);
+ throw re.rethrowFromSystemServer();
}
}
@@ -525,7 +524,7 @@ public final class UiAutomation {
// Calling out without a lock held.
mUiAutomationConnection.dropShellPermissionIdentity();
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error executing dropping shell permission identity!", re);
+ throw re.rethrowFromSystemServer();
}
}
@@ -543,8 +542,7 @@ public final class UiAutomation {
final List<String> permissions = mUiAutomationConnection.getAdoptedShellPermissions();
return permissions == null ? ALL_PERMISSIONS : new ArraySet<>(permissions);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error getting adopted shell permissions", re);
- return Collections.emptySet();
+ throw re.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 128a8721f1e7..8ebd329484b2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3925,6 +3925,8 @@ public class DevicePolicyManager {
* <p>The MTE policy can only be set to {@link #MTE_DISABLED} if called by a device owner.
* Otherwise a {@link SecurityException} will be thrown.
*
+ * <p>The device needs to be rebooted to apply changes to the MTE policy.
+ *
* @throws SecurityException if caller is not device owner or profile owner of org-owned device
* or if called on a parent instance
* @param policy the MTE policy to be set
@@ -6311,13 +6313,12 @@ public class DevicePolicyManager {
* personal use, removing the managed profile and all policies set by the profile owner.
* </p>
*
- * <p>
- * Calling this method from the primary user will only work if the calling app is targeting
- * Android 13 or below, in which case it will cause the device to reboot, erasing all device
- * data - including all the secondary users and their data - while booting up. If an app
- * targeting Android 13+ is calling this method from the primary user or last full user,
- * {@link IllegalStateException} will be thrown.
- * </p>
+ * <p> Calling this method from the primary user will only work if the calling app is
+ * targeting SDK level {@link Build.VERSION_CODES#TIRAMISU} or below, in which case it will
+ * cause the device to reboot, erasing all device data - including all the secondary users
+ * and their data - while booting up. If an app targeting SDK level
+ * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above is calling this method from the
+ * primary user or last full user, {@link IllegalStateException} will be thrown. </p>
*
* If an app wants to wipe the entire device irrespective of which user they are from, they
* should use {@link #wipeDevice} instead.
@@ -6453,6 +6454,10 @@ public class DevicePolicyManager {
* <p>The caller must hold the
* {@link android.Manifest.permission#TRIGGER_LOST_MODE} permission.
*
+ * <p>Register a broadcast receiver to receive lost mode location updates. This receiver should
+ * subscribe to the {@link #ACTION_LOST_MODE_LOCATION_UPDATE} action and receive the location
+ * from an intent extra {@link #EXTRA_LOST_MODE_LOCATION}.
+ *
* <p> Not for use by third-party applications.
*
* @param executor The executor through which the callback should be invoked.
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 92f7dee0b0ad..cb6aa09cc6db 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -31,6 +31,7 @@ import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.util.IntArray;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -43,6 +44,7 @@ import java.util.List;
* @hide
*/
public class TransactionExecutorHelper {
+ private static final String TAG = TransactionExecutorHelper.class.getSimpleName();
// A penalty applied to path with destruction when looking for the shortest one.
private static final int DESTRUCTION_PENALTY = 10;
@@ -162,6 +164,11 @@ public class TransactionExecutorHelper {
if (finalStates == null || finalStates.length == 0) {
return UNDEFINED;
}
+ if (r == null) {
+ // Early return because the ActivityClientRecord hasn't been created or cannot be found.
+ Log.w(TAG, "ActivityClientRecord was null");
+ return UNDEFINED;
+ }
final int currentState = r.getLifecycleState();
int closestState = UNDEFINED;
diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl
index 6b9d2c73450e..e4ac01195bcb 100644
--- a/core/java/android/app/trust/ITrustListener.aidl
+++ b/core/java/android/app/trust/ITrustListener.aidl
@@ -24,8 +24,8 @@ import java.util.List;
* {@hide}
*/
oneway interface ITrustListener {
- void onTrustChanged(boolean enabled, int userId, int flags,
+ void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags,
in List<String> trustGrantedMessages);
void onTrustManagedChanged(boolean managed, int userId);
void onTrustError(in CharSequence message);
-} \ No newline at end of file
+}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 9e825b7207e0..62f755d0268c 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -22,6 +22,7 @@ import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.hardware.biometrics.BiometricSourceType;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -45,6 +46,7 @@ public class TrustManager {
private static final String TAG = "TrustManager";
private static final String DATA_FLAGS = "initiatedByUser";
+ private static final String DATA_NEWLY_UNLOCKED = "newlyUnlocked";
private static final String DATA_MESSAGE = "message";
private static final String DATA_GRANTED_MESSAGES = "grantedMessages";
@@ -171,13 +173,14 @@ public class TrustManager {
try {
ITrustListener.Stub iTrustListener = new ITrustListener.Stub() {
@Override
- public void onTrustChanged(boolean enabled, int userId, int flags,
- List<String> trustGrantedMessages) {
+ public void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId,
+ int flags, List<String> trustGrantedMessages) {
Message m = mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId,
trustListener);
if (flags != 0) {
m.getData().putInt(DATA_FLAGS, flags);
}
+ m.getData().putInt(DATA_NEWLY_UNLOCKED, newlyUnlocked ? 1 : 0);
m.getData().putCharSequenceArrayList(
DATA_GRANTED_MESSAGES, (ArrayList) trustGrantedMessages);
m.sendToTarget();
@@ -265,9 +268,14 @@ public class TrustManager {
public void handleMessage(Message msg) {
switch(msg.what) {
case MSG_TRUST_CHANGED:
- int flags = msg.peekData() != null ? msg.peekData().getInt(DATA_FLAGS) : 0;
- ((TrustListener) msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2, flags,
- msg.getData().getStringArrayList(DATA_GRANTED_MESSAGES));
+ Bundle data = msg.peekData();
+ int flags = data != null ? data.getInt(DATA_FLAGS) : 0;
+ boolean enabled = msg.arg1 != 0;
+ int newlyUnlockedInt =
+ data != null ? data.getInt(DATA_NEWLY_UNLOCKED) : 0;
+ boolean newlyUnlocked = newlyUnlockedInt != 0;
+ ((TrustListener) msg.obj).onTrustChanged(enabled, newlyUnlocked, msg.arg2,
+ flags, msg.getData().getStringArrayList(DATA_GRANTED_MESSAGES));
break;
case MSG_TRUST_MANAGED_CHANGED:
((TrustListener)msg.obj).onTrustManagedChanged(msg.arg1 != 0, msg.arg2);
@@ -284,6 +292,8 @@ public class TrustManager {
/**
* Reports that the trust state has changed.
* @param enabled If true, the system believes the environment to be trusted.
+ * @param newlyUnlocked If true, the system believes the device is newly unlocked due
+ * to the trust changing.
* @param userId The user, for which the trust changed.
* @param flags Flags specified by the trust agent when granting trust. See
* {@link android.service.trust.TrustAgentService#grantTrust(CharSequence, long, int)
@@ -291,7 +301,7 @@ public class TrustManager {
* @param trustGrantedMessages Messages to display to the user when trust has been granted
* by one or more trust agents.
*/
- void onTrustChanged(boolean enabled, int userId, int flags,
+ void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags,
List<String> trustGrantedMessages);
/**
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index f17d18652e98..22ea9f2094cb 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -17,11 +17,13 @@
package android.companion.virtual;
import android.app.PendingIntent;
+import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.IVirtualSensorStateChangeCallback;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.content.IntentFilter;
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.input.VirtualDpadConfig;
@@ -125,4 +127,15 @@ interface IVirtualDevice {
/** Sets whether to show or hide the cursor while this virtual device is active. */
void setShowPointerIcon(boolean showPointerIcon);
+
+ /**
+ * Registers an intent interceptor that will intercept an intent attempting to launch
+ * when matching the provided IntentFilter and calls the callback with the intercepted
+ * intent.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)")
+ void registerIntentInterceptor(
+ in IVirtualDeviceIntentInterceptor intentInterceptor, in IntentFilter filter);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)")
+ void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor);
}
diff --git a/core/java/android/companion/virtual/IVirtualDeviceIntentInterceptor.aidl b/core/java/android/companion/virtual/IVirtualDeviceIntentInterceptor.aidl
new file mode 100644
index 000000000000..cd43bf746ecf
--- /dev/null
+++ b/core/java/android/companion/virtual/IVirtualDeviceIntentInterceptor.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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 android.companion.virtual;
+
+import android.content.Intent;
+
+/**
+ * Interceptor interface to be called when an intent matches the IntentFilter passed into {@link
+ * VirtualDevice#registerIntentInterceptor}. When the interceptor is called after matching the
+ * IntentFilter, the intended activity launch will be aborted and alternatively replaced by
+ * the interceptor's receiver.
+ *
+ * @hide
+ */
+oneway interface IVirtualDeviceIntentInterceptor {
+
+ /**
+ * Called when an intent that matches the IntentFilter registered in {@link
+ * VirtualDevice#registerIntentInterceptor} is intercepted for the virtual device to
+ * handle.
+ *
+ * @param intent The intent that has been intercepted by the interceptor.
+ */
+ void onIntentIntercepted(in Intent intent);
+}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index dba7c8e630c8..cc452e27a650 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -35,6 +35,8 @@ import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Point;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.display.DisplayManager;
@@ -269,6 +271,9 @@ public final class VirtualDeviceManager {
private final IVirtualDevice mVirtualDevice;
private final ArrayMap<ActivityListener, ActivityListenerDelegate> mActivityListeners =
new ArrayMap<>();
+ private final ArrayMap<IntentInterceptorCallback,
+ VirtualIntentInterceptorDelegate> mIntentInterceptorListeners =
+ new ArrayMap<>();
private final IVirtualDeviceActivityListener mActivityListenerBinder =
new IVirtualDeviceActivityListener.Stub() {
@@ -857,6 +862,53 @@ public final class VirtualDeviceManager {
public void removeActivityListener(@NonNull ActivityListener listener) {
mActivityListeners.remove(listener);
}
+
+ /**
+ * Registers an intent interceptor that will intercept an intent attempting to launch
+ * when matching the provided IntentFilter and calls the callback with the intercepted
+ * intent.
+ *
+ * @param executor The executor where the interceptor is executed on.
+ * @param interceptorFilter The filter to match intents intended for interception.
+ * @param interceptorCallback The callback called when an intent matching interceptorFilter
+ * is intercepted.
+ * @see #unregisterIntentInterceptor(IntentInterceptorCallback)
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void registerIntentInterceptor(
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull IntentFilter interceptorFilter,
+ @NonNull IntentInterceptorCallback interceptorCallback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(interceptorFilter);
+ Objects.requireNonNull(interceptorCallback);
+ final VirtualIntentInterceptorDelegate delegate =
+ new VirtualIntentInterceptorDelegate(executor, interceptorCallback);
+ try {
+ mVirtualDevice.registerIntentInterceptor(delegate, interceptorFilter);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mIntentInterceptorListeners.put(interceptorCallback, delegate);
+ }
+
+ /**
+ * Unregisters the intent interceptorCallback previously registered with
+ * {@link #registerIntentInterceptor}.
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void unregisterIntentInterceptor(
+ @NonNull IntentInterceptorCallback interceptorCallback) {
+ Objects.requireNonNull(interceptorCallback);
+ final VirtualIntentInterceptorDelegate delegate =
+ mIntentInterceptorListeners.get(interceptorCallback);
+ try {
+ mVirtualDevice.unregisterIntentInterceptor(delegate);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mIntentInterceptorListeners.remove(interceptorCallback);
+ }
}
/**
@@ -908,4 +960,51 @@ public final class VirtualDeviceManager {
mExecutor.execute(() -> mActivityListener.onDisplayEmpty(displayId));
}
}
+
+ /**
+ * Interceptor interface to be called when an intent matches the IntentFilter passed into {@link
+ * VirtualDevice#registerIntentInterceptor}. When the interceptor is called after matching the
+ * IntentFilter, the intended activity launch will be aborted and alternatively replaced by
+ * the interceptor's receiver.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface IntentInterceptorCallback {
+
+ /**
+ * Called when an intent that matches the IntentFilter registered in {@link
+ * VirtualDevice#registerIntentInterceptor} is intercepted for the virtual device to
+ * handle.
+ *
+ * @param intent The intent that has been intercepted by the interceptor.
+ */
+ void onIntentIntercepted(@NonNull Intent intent);
+ }
+
+ /**
+ * A wrapper for {@link IntentInterceptorCallback} that executes callbacks on the
+ * the given executor.
+ */
+ private static class VirtualIntentInterceptorDelegate
+ extends IVirtualDeviceIntentInterceptor.Stub {
+ @NonNull private final IntentInterceptorCallback mIntentInterceptorCallback;
+ @NonNull private final Executor mExecutor;
+
+ private VirtualIntentInterceptorDelegate(Executor executor,
+ IntentInterceptorCallback interceptorCallback) {
+ mExecutor = executor;
+ mIntentInterceptorCallback = interceptorCallback;
+ }
+
+ @Override
+ public void onIntentIntercepted(Intent intent) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mIntentInterceptorCallback.onIntentIntercepted(intent));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 382e2bb6ee43..bb3bf5cc2b18 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3045,6 +3045,14 @@ public abstract class Context {
* Prior to that, it would be ignored and delivered to all matching registered
* receivers. Be careful if using this for security.</p>
*
+ * <p>For apps targeting {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * either {@link #RECEIVER_EXPORTED} or {@link #RECEIVER_NOT_EXPORTED} must be
+ * specified if the receiver is not being registered for <a
+ * href="{@docRoot}guide/components/broadcasts#system-broadcasts">system broadcasts</a>
+ * or a {@link SecurityException} will be thrown. See {@link
+ * #registerReceiver(BroadcastReceiver, IntentFilter, int)} to register a receiver with
+ * flags.
+ *
* <p class="note">Note: this method <em>cannot be called from a
* {@link BroadcastReceiver} component;</em> that is, from a BroadcastReceiver
* that is declared in an application's manifest. It is okay, however, to call
@@ -3084,16 +3092,16 @@ public abstract class Context {
*
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
- * @param flags Additional options for the receiver. In a future release, either
- * {@link #RECEIVER_EXPORTED} or {@link #RECEIVER_NOT_EXPORTED} must be specified if the
- * receiver isn't being registered for <a href="{@docRoot}guide/components
- * /broadcasts#system-broadcasts">system broadcasts</a> or an exception will be
- * thrown. If {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
- * specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
- * system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
- * Android SDK. If both {@link #RECEIVER_EXPORTED} and
- * {@link #RECEIVER_NOT_EXPORTED} are specified, an exception will be thrown as
- * well.
+ * @param flags Additional options for the receiver. For apps targeting {@link
+ * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} either {@link #RECEIVER_EXPORTED} or
+ * {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
+ * for <a href="{@docRoot}guide/components/broadcasts#system-broadcasts">system
+ * broadcasts</a> or a {@link SecurityException} will be thrown. If {@link
+ * #RECEIVER_EXPORTED} is specified, a receiver may additionally specify {@link
+ * #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of system broadcast actions,
+ * see the BROADCAST_ACTIONS.TXT file in the Android SDK. If both {@link
+ * #RECEIVER_EXPORTED} and {@link #RECEIVER_NOT_EXPORTED} are specified, an {@link
+ * IllegalArgumentException} will be thrown.
*
* @return The first sticky intent found that matches <var>filter</var>,
* or null if there are none.
@@ -3123,6 +3131,14 @@ public abstract class Context {
* Prior to that, it would be ignored and delivered to all matching registered
* receivers. Be careful if using this for security.</p>
*
+ * <p>For apps targeting {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * either {@link #RECEIVER_EXPORTED} or {@link #RECEIVER_NOT_EXPORTED} must be
+ * specified if the receiver is not being registered for <a
+ * href="{@docRoot}guide/components/broadcasts#system-broadcasts">system broadcasts</a>
+ * or a {@link SecurityException} will be thrown. See {@link
+ * #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler, int)} to register a
+ * receiver with flags.
+ *
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
* @param broadcastPermission String naming a permissions that a
@@ -3165,17 +3181,16 @@ public abstract class Context {
* no permission is required.
* @param scheduler Handler identifying the thread that will receive
* the Intent. If null, the main thread of the process will be used.
- * @param flags Additional options for the receiver. In a future release, either
- * {@link #RECEIVER_EXPORTED} or {@link #RECEIVER_NOT_EXPORTED} must be specified if the
- * receiver isn't being registered for <a href="{@docRoot}guide/components
- * /broadcasts#system-broadcasts">system broadcasts</a> or an exception will be
- * thrown. If {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
- * specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
- * system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
- * Android SDK. If both {@link #RECEIVER_EXPORTED} and
- * {@link #RECEIVER_NOT_EXPORTED} are specified, an exception will be thrown as
- * well.
- *
+ * @param flags Additional options for the receiver. For apps targeting {@link
+ * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} either {@link #RECEIVER_EXPORTED} or
+ * {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
+ * for <a href="{@docRoot}guide/components/broadcasts#system-broadcasts">system
+ * broadcasts</a> or a {@link SecurityException} will be thrown. If {@link
+ * #RECEIVER_EXPORTED} is specified, a receiver may additionally specify {@link
+ * #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of system broadcast actions,
+ * see the BROADCAST_ACTIONS.TXT file in the Android SDK. If both {@link
+ * #RECEIVER_EXPORTED} and {@link #RECEIVER_NOT_EXPORTED} are specified, an {@link
+ * IllegalArgumentException} will be thrown.
* @return The first sticky intent found that matches <var>filter</var>,
* or null if there are none.
*
@@ -3233,16 +3248,16 @@ public abstract class Context {
* no permission is required.
* @param scheduler Handler identifying the thread that will receive
* the Intent. If {@code null}, the main thread of the process will be used.
- * @param flags Additional options for the receiver. In a future release, either
- * {@link #RECEIVER_EXPORTED} or {@link #RECEIVER_NOT_EXPORTED} must be specified if the
- * receiver isn't being registered for <a href="{@docRoot}guide/components
- * /broadcasts#system-broadcasts">system broadcasts</a> or an exception will be
- * thrown. If {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
- * specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
- * system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
- * Android SDK. If both {@link #RECEIVER_EXPORTED} and
- * {@link #RECEIVER_NOT_EXPORTED} are specified, an exception will be thrown as
- * well.
+ * @param flags Additional options for the receiver. For apps targeting {@link
+ * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} either {@link #RECEIVER_EXPORTED} or
+ * {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
+ * for <a href="{@docRoot}guide/components/broadcasts#system-broadcasts">system
+ * broadcasts</a> or a {@link SecurityException} will be thrown. If {@link
+ * #RECEIVER_EXPORTED} is specified, a receiver may additionally specify {@link
+ * #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of system broadcast actions,
+ * see the BROADCAST_ACTIONS.TXT file in the Android SDK. If both {@link
+ * #RECEIVER_EXPORTED} and {@link #RECEIVER_NOT_EXPORTED} are specified, an {@link
+ * IllegalArgumentException} will be thrown.
*
* @return The first sticky intent found that matches <var>filter</var>,
* or {@code null} if there are none.
@@ -3306,16 +3321,16 @@ public abstract class Context {
* no permission is required.
* @param scheduler Handler identifying the thread that will receive
* the Intent. If null, the main thread of the process will be used.
- * @param flags Additional options for the receiver. In a future release, either
- * {@link #RECEIVER_EXPORTED} or {@link #RECEIVER_NOT_EXPORTED} must be specified if the
- * receiver isn't being registered for <a href="{@docRoot}guide/components
- * /broadcasts#system-broadcasts">system broadcasts</a> or an exception will be
- * thrown. If {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
- * specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
- * system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
- * Android SDK. If both {@link #RECEIVER_EXPORTED} and
- * {@link #RECEIVER_NOT_EXPORTED} are specified, an exception will be thrown as
- * well.
+ * @param flags Additional options for the receiver. For apps targeting {@link
+ * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} either {@link #RECEIVER_EXPORTED} or
+ * {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
+ * for <a href="{@docRoot}guide/components/broadcasts#system-broadcasts">system
+ * broadcasts</a> or a {@link SecurityException} will be thrown. If {@link
+ * #RECEIVER_EXPORTED} is specified, a receiver may additionally specify {@link
+ * #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of system broadcast actions,
+ * see the BROADCAST_ACTIONS.TXT file in the Android SDK. If both {@link
+ * #RECEIVER_EXPORTED} and {@link #RECEIVER_NOT_EXPORTED} are specified, an {@link
+ * IllegalArgumentException} will be thrown.
*
* @return The first sticky intent found that matches <var>filter</var>,
* or null if there are none.
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 7d9c64add492..60a7b13ff6e9 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -63,4 +63,7 @@ interface IPackageInstallerSession {
void requestUserPreapproval(in PackageInstaller.PreapprovalDetails details, in IntentSender statusReceiver);
boolean isKeepApplicationEnabledSetting();
+
+ ParcelFileDescriptor getAppMetadataFd();
+ ParcelFileDescriptor openWriteAppMetadata();
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 81bea2e3573f..54ca1e59aafe 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -159,6 +159,8 @@ interface IPackageManager {
*/
ParceledListSlice getInstalledPackages(long flags, in int userId);
+ String getAppMetadataPath(String packageName, int userId);
+
/**
* This implements getPackagesHoldingPermissions via a "last returned row"
* mechanism that is not exposed in the API. This is to get around the IPC
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f17d8fac9ede..8deecd7f0b1b 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -59,6 +59,7 @@ import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.ParcelableException;
+import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -1760,6 +1761,65 @@ public class PackageInstaller {
}
/**
+ * @return A PersistableBundle containing the app metadata set with
+ * {@link Session#setAppMetadata(PersistableBundle)}. In the case where this data does not
+ * exist, an empty PersistableBundle is returned.
+ */
+ @NonNull
+ public PersistableBundle getAppMetadata() {
+ PersistableBundle data = null;
+ try {
+ ParcelFileDescriptor pfd = mSession.getAppMetadataFd();
+ if (pfd != null) {
+ try (InputStream inputStream =
+ new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ data = PersistableBundle.readFromStream(inputStream);
+ }
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return data != null ? data : new PersistableBundle();
+ }
+
+ private OutputStream openWriteAppMetadata() throws IOException {
+ try {
+ if (ENABLE_REVOCABLE_FD) {
+ return new ParcelFileDescriptor.AutoCloseOutputStream(
+ mSession.openWriteAppMetadata());
+ } else {
+ final ParcelFileDescriptor clientSocket = mSession.openWriteAppMetadata();
+ return new FileBridge.FileBridgeOutputStream(clientSocket);
+ }
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Optionally set the app metadata. The size of this data cannot exceed the maximum allowed.
+ * If no data is provided, then any existing app metadata from the previous install will be
+ * removed for the package.
+ *
+ * @param data a PersistableBundle containing the app metadata. If this is set to null then
+ * any existing app metadata will be removed.
+ * @throws IOException if writing the data fails.
+ */
+ public void setAppMetadata(@Nullable PersistableBundle data) throws IOException {
+ if (data == null) {
+ return;
+ }
+ try (OutputStream outputStream = openWriteAppMetadata()) {
+ data.writeToStream(outputStream);
+ }
+ }
+
+ /**
* Attempt to request the approval before committing this session.
*
* For installers that have been granted the
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f3ccfb0ac4f2..b6ca15932a90 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5753,6 +5753,24 @@ public abstract class PackageManager {
}
/**
+ * Returns the app metadata for a package.
+ *
+ * @param packageName
+ * @return A PersistableBundle containing the app metadata that was provided by the installer.
+ * In the case where a package does not have any metadata, an empty PersistableBundle is
+ * returned.
+ * @throws NameNotFoundException if no such package is available to the caller.
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(Manifest.permission.GET_APP_METADATA)
+ public PersistableBundle getAppMetadata(@NonNull String packageName)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException("getAppMetadata not implemented in subclass");
+ }
+
+ /**
* Return a List of all installed packages that are currently holding any of
* the given permissions.
*
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index fb61b3769e98..7f0f44bd36eb 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -48,6 +48,8 @@ public final class UserProperties implements Parcelable {
private static final String ATTR_USE_PARENTS_CONTACTS = "useParentsContacts";
private static final String ATTR_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA =
"updateCrossProfileIntentFiltersOnOTA";
+ private static final String ATTR_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL =
+ "crossProfileIntentFilterAccessControl";
/** Index values of each property (to indicate whether they are present in this object). */
@IntDef(prefix = "INDEX_", value = {
@@ -56,7 +58,8 @@ public final class UserProperties implements Parcelable {
INDEX_SHOW_IN_SETTINGS,
INDEX_INHERIT_DEVICE_POLICY,
INDEX_USE_PARENTS_CONTACTS,
- INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA
+ INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA,
+ INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -67,6 +70,7 @@ public final class UserProperties implements Parcelable {
private static final int INDEX_INHERIT_DEVICE_POLICY = 3;
private static final int INDEX_USE_PARENTS_CONTACTS = 4;
private static final int INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA = 5;
+ private static final int INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL = 6;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -170,6 +174,51 @@ public final class UserProperties implements Parcelable {
private final @Nullable UserProperties mDefaultProperties;
/**
+ * CrossProfileIntentFilterAccessControlLevel provides level of access for user to create/modify
+ * {@link CrossProfileIntentFilter}. Each level have value assigned, the higher the value
+ * implies higher restriction for creation/modification.
+ * CrossProfileIntentFilterAccessControlLevel allows us to protect against malicious changes in
+ * user's {@link CrossProfileIntentFilter}s, which might add/remove
+ * {@link CrossProfileIntentFilter} leading to unprecedented results.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_"}, value = {
+ CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_ALL,
+ CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM,
+ CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM_ADD_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CrossProfileIntentFilterAccessControlLevel {
+ }
+
+ /**
+ * CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_ALL signifies that irrespective of user we would
+ * allow access (addition/modification/removal) for CrossProfileIntentFilter.
+ * This is the default access control level.
+ *
+ * @hide
+ */
+ public static final int CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_ALL = 0;
+
+ /**
+ * CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM signifies that only system/root user would
+ * be able to access (addition/modification/removal) CrossProfileIntentFilter.
+ *
+ * @hide
+ */
+ public static final int CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM = 10;
+
+ /**
+ * CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM_ADD_ONLY signifies that only system/root
+ * user would be able to add CrossProfileIntentFilter but not modify/remove. Once added, it
+ * cannot be modified or removed.
+ *
+ * @hide
+ */
+ public static final int CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM_ADD_ONLY = 20;
+
+ /**
* Creates a UserProperties (intended for the SystemServer) that stores a reference to the given
* default properties, which it uses for any property not subsequently set.
* @hide
@@ -204,6 +253,8 @@ public final class UserProperties implements Parcelable {
setStartWithParent(orig.getStartWithParent());
setInheritDevicePolicy(orig.getInheritDevicePolicy());
setUpdateCrossProfileIntentFiltersOnOTA(orig.getUpdateCrossProfileIntentFiltersOnOTA());
+ setCrossProfileIntentFilterAccessControl(
+ orig.getCrossProfileIntentFilterAccessControl());
}
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
@@ -387,6 +438,34 @@ public final class UserProperties implements Parcelable {
*/
private boolean mUpdateCrossProfileIntentFiltersOnOTA;
+
+ /**
+ * Returns the user's {@link CrossProfileIntentFilterAccessControlLevel}.
+ * @hide
+ */
+ public @CrossProfileIntentFilterAccessControlLevel int
+ getCrossProfileIntentFilterAccessControl() {
+ if (isPresent(INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL)) {
+ return mCrossProfileIntentFilterAccessControl;
+ }
+ if (mDefaultProperties != null) {
+ return mDefaultProperties.mCrossProfileIntentFilterAccessControl;
+ }
+ throw new SecurityException("You don't have permission to query "
+ + "crossProfileIntentFilterAccessControl");
+ }
+ /**
+ * Sets {@link CrossProfileIntentFilterAccessControlLevel} for the user.
+ * @param val access control for user
+ * @hide
+ */
+ public void setCrossProfileIntentFilterAccessControl(
+ @CrossProfileIntentFilterAccessControlLevel int val) {
+ this.mCrossProfileIntentFilterAccessControl = val;
+ setPresent(INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL);
+ }
+ private @CrossProfileIntentFilterAccessControlLevel int mCrossProfileIntentFilterAccessControl;
+
@Override
public String toString() {
// Please print in increasing order of PropertyIndex.
@@ -399,6 +478,8 @@ public final class UserProperties implements Parcelable {
+ ", mUseParentsContacts=" + getUseParentsContacts()
+ ", mUpdateCrossProfileIntentFiltersOnOTA="
+ getUpdateCrossProfileIntentFiltersOnOTA()
+ + ", mCrossProfileIntentFilterAccessControl="
+ + getCrossProfileIntentFilterAccessControl()
+ "}";
}
@@ -417,6 +498,8 @@ public final class UserProperties implements Parcelable {
pw.println(prefix + " mUseParentsContacts=" + getUseParentsContacts());
pw.println(prefix + " mUpdateCrossProfileIntentFiltersOnOTA="
+ getUpdateCrossProfileIntentFiltersOnOTA());
+ pw.println(prefix + " mCrossProfileIntentFilterAccessControl="
+ + getCrossProfileIntentFilterAccessControl());
}
/**
@@ -468,6 +551,9 @@ public final class UserProperties implements Parcelable {
case ATTR_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA:
setUpdateCrossProfileIntentFiltersOnOTA(parser.getAttributeBoolean(i));
break;
+ case ATTR_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL:
+ setCrossProfileIntentFilterAccessControl(parser.getAttributeInt(i));
+ break;
default:
Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
}
@@ -507,6 +593,10 @@ public final class UserProperties implements Parcelable {
ATTR_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA,
mUpdateCrossProfileIntentFiltersOnOTA);
}
+ if (isPresent(INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL)) {
+ serializer.attributeInt(null, ATTR_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL,
+ mCrossProfileIntentFilterAccessControl);
+ }
}
// For use only with an object that has already had any permission-lacking fields stripped out.
@@ -519,6 +609,7 @@ public final class UserProperties implements Parcelable {
dest.writeInt(mInheritDevicePolicy);
dest.writeBoolean(mUseParentsContacts);
dest.writeBoolean(mUpdateCrossProfileIntentFiltersOnOTA);
+ dest.writeInt(mCrossProfileIntentFilterAccessControl);
}
/**
@@ -535,6 +626,7 @@ public final class UserProperties implements Parcelable {
mInheritDevicePolicy = source.readInt();
mUseParentsContacts = source.readBoolean();
mUpdateCrossProfileIntentFiltersOnOTA = source.readBoolean();
+ mCrossProfileIntentFilterAccessControl = source.readInt();
}
@Override
@@ -565,6 +657,9 @@ public final class UserProperties implements Parcelable {
private @InheritDevicePolicy int mInheritDevicePolicy = INHERIT_DEVICE_POLICY_NO;
private boolean mUseParentsContacts = false;
private boolean mUpdateCrossProfileIntentFiltersOnOTA = false;
+ private @CrossProfileIntentFilterAccessControlLevel int
+ mCrossProfileIntentFilterAccessControl =
+ CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_ALL;
public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
mShowInLauncher = showInLauncher;
@@ -601,6 +696,14 @@ public final class UserProperties implements Parcelable {
return this;
}
+ /** Sets the value for {@link #mCrossProfileIntentFilterAccessControl} */
+ public Builder setCrossProfileIntentFilterAccessControl(
+ @CrossProfileIntentFilterAccessControlLevel int
+ crossProfileIntentFilterAccessControl) {
+ mCrossProfileIntentFilterAccessControl = crossProfileIntentFilterAccessControl;
+ return this;
+ }
+
/** Builds a UserProperties object with *all* values populated. */
public UserProperties build() {
return new UserProperties(
@@ -609,7 +712,8 @@ public final class UserProperties implements Parcelable {
mShowInSettings,
mInheritDevicePolicy,
mUseParentsContacts,
- mUpdateCrossProfileIntentFiltersOnOTA);
+ mUpdateCrossProfileIntentFiltersOnOTA,
+ mCrossProfileIntentFilterAccessControl);
}
} // end Builder
@@ -619,7 +723,8 @@ public final class UserProperties implements Parcelable {
boolean startWithParent,
@ShowInSettings int showInSettings,
@InheritDevicePolicy int inheritDevicePolicy,
- boolean useParentsContacts, boolean updateCrossProfileIntentFiltersOnOTA) {
+ boolean useParentsContacts, boolean updateCrossProfileIntentFiltersOnOTA,
+ @CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl) {
mDefaultProperties = null;
setShowInLauncher(showInLauncher);
@@ -628,5 +733,6 @@ public final class UserProperties implements Parcelable {
setInheritDevicePolicy(inheritDevicePolicy);
setUseParentsContacts(useParentsContacts);
setUpdateCrossProfileIntentFiltersOnOTA(updateCrossProfileIntentFiltersOnOTA);
+ setCrossProfileIntentFilterAccessControl(crossProfileIntentFilterAccessControl);
}
}
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 49e849523de2..8b614d87a0da 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -31,6 +31,7 @@ import android.os.CancellationSignal;
import android.os.ICancellationSignal;
import android.os.OutcomeReceiver;
import android.os.RemoteException;
+import android.provider.DeviceConfig;
import android.util.Log;
import java.util.List;
@@ -49,6 +50,8 @@ import java.util.concurrent.Executor;
@SystemService(Context.CREDENTIAL_SERVICE)
public final class CredentialManager {
private static final String TAG = "CredentialManager";
+ private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER =
+ "enable_credential_manager";
private final Context mContext;
private final ICredentialManager mService;
@@ -205,11 +208,7 @@ public final class CredentialManager {
* @param callback the callback invoked when the request succeeds or fails
* @hide
*/
- @RequiresPermission(
- allOf = {
- android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS,
- android.Manifest.permission.QUERY_ALL_PACKAGES
- })
+ @RequiresPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)
public void listEnabledProviders(
@Nullable CancellationSignal cancellationSignal,
@CallbackExecutor @NonNull Executor executor,
@@ -266,6 +265,16 @@ public final class CredentialManager {
}
}
+ /**
+ * Returns whether the service is enabled.
+ *
+ * @hide
+ */
+ public static boolean isServiceEnabled() {
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER, true);
+ }
+
private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
// TODO: listen for cancellation to release callback.
@@ -397,7 +406,7 @@ public final class CredentialManager {
public void onError(String errorType, String message) {
mExecutor.execute(
() -> mCallback.onError(new ListEnabledProvidersException(errorType, message)));
- }
+ }
}
private static class SetEnabledProvidersTransport extends ISetEnabledProvidersCallback.Stub {
diff --git a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
index 2e8e763b0ee4..18979a9f78eb 100644
--- a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
@@ -17,7 +17,7 @@ package android.hardware.biometrics;
/**
* A secondary communication channel from AuthController back to BiometricService for
- * events that are not associated with an autentication session. See
+ * events that are not associated with an authentication session. See
* {@link IBiometricSysuiReceiver} for events associated with a session.
*
* @hide
@@ -27,4 +27,16 @@ oneway interface IBiometricContextListener {
// These may be called while the device is still transitioning to the new state
// (i.e. about to become awake or enter doze)
void onDozeChanged(boolean isDozing, boolean isAwake);
+
+ @VintfStability
+ @Backing(type="int")
+ enum FoldState {
+ UNKNOWN = 0,
+ HALF_OPENED = 1,
+ FULLY_OPENED = 2,
+ FULLY_CLOSED = 3,
+ }
+
+ // Called when the fold state of the device changes.
+ void onFoldChanged(FoldState FoldState);
}
diff --git a/core/java/android/hardware/input/VirtualKeyboardConfig.java b/core/java/android/hardware/input/VirtualKeyboardConfig.java
index 94638574c82d..28d8a0ff553e 100644
--- a/core/java/android/hardware/input/VirtualKeyboardConfig.java
+++ b/core/java/android/hardware/input/VirtualKeyboardConfig.java
@@ -18,9 +18,12 @@ package android.hardware.input;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.icu.util.ULocale;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Configurations to create virtual keyboard.
@@ -29,6 +32,18 @@ import android.os.Parcelable;
*/
@SystemApi
public final class VirtualKeyboardConfig extends VirtualInputDeviceConfig implements Parcelable {
+ /**
+ * Default language tag when creating virtual keyboard. Used when the language tag is not set.
+ */
+ public static final String DEFAULT_LANGUAGE_TAG = "en-Latn-US";
+ /** Default layout type when creating virtual keyboard. Used when the layout type is not set. */
+ public static final String DEFAULT_LAYOUT_TYPE = "qwerty";
+
+ @NonNull
+ private final String mLanguageTag;
+
+ @NonNull
+ private final String mLayoutType;
@NonNull
public static final Creator<VirtualKeyboardConfig> CREATOR =
@@ -46,10 +61,30 @@ public final class VirtualKeyboardConfig extends VirtualInputDeviceConfig implem
private VirtualKeyboardConfig(@NonNull Builder builder) {
super(builder);
+ mLanguageTag = builder.mLanguageTag;
+ mLayoutType = builder.mLayoutType;
}
private VirtualKeyboardConfig(@NonNull Parcel in) {
super(in);
+ mLanguageTag = in.readString8();
+ mLayoutType = in.readString8();
+ }
+
+ /**
+ * @see Builder#setLanguageTag().
+ */
+ @NonNull
+ public String getLanguageTag() {
+ return mLanguageTag;
+ }
+
+ /**
+ * @see Builder#setLayoutType().
+ */
+ @NonNull
+ public String getLayoutType() {
+ return mLayoutType;
}
@Override
@@ -60,12 +95,66 @@ public final class VirtualKeyboardConfig extends VirtualInputDeviceConfig implem
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
+ dest.writeString8(mLanguageTag);
+ dest.writeString8(mLayoutType);
}
/**
* Builder for creating a {@link VirtualKeyboardConfig}.
*/
public static final class Builder extends VirtualInputDeviceConfig.Builder<Builder> {
+ @NonNull
+ private String mLanguageTag = DEFAULT_LANGUAGE_TAG;
+ @NonNull
+ private String mLayoutType = DEFAULT_LAYOUT_TYPE;
+
+ /**
+ * Sets the preferred input language of the virtual keyboard using an IETF
+ * <a href="https://tools.ietf.org/html/bcp47">BCP-47</a>
+ * conformant tag. See {@code keyboardLocale} attribute in
+ * frameworks/base/packages/InputDevices/res/xml/keyboard_layouts.xml for a list of
+ * supported language tags.
+ *
+ * The passed in {@code languageTag} will be canonized using {@link
+ * ULocale} and used by the system as a hint to configure the keyboard layout.
+ *
+ * If {@code languageTag} is not specified, the virtual keyboard will be created with {@link
+ * #DEFAULT_LANGUAGE_TAG}.
+ *
+ * Note that the preferred layout is not guaranteed. If the specified language is
+ * well-formed but not supported, the keyboard will be using English US QWERTY layout.
+ *
+ * @throws IllegalArgumentException if either of the language or country is not present in
+ * the language tag.
+ */
+ @NonNull
+ public Builder setLanguageTag(@NonNull String languageTag) {
+ Objects.requireNonNull(languageTag, "languageTag cannot be null");
+ ULocale locale = ULocale.forLanguageTag(languageTag);
+ if (locale.getLanguage().isEmpty() || locale.getCountry().isEmpty()) {
+ throw new IllegalArgumentException("The language tag is not valid.");
+ }
+ mLanguageTag = ULocale.createCanonical(locale).toLanguageTag();
+ return this;
+ }
+
+ /**
+ * Sets the preferred layout type of the virtual keyboard. See {@code keyboardLayoutType}
+ * attribute in frameworks/base/packages/InputDevices/res/xml/keyboard_layouts.xml for a
+ * list of supported layout types.
+ *
+ * Note that the preferred layout is not guaranteed. If the specified layout type is
+ * well-formed but not supported, the keyboard will be using English US QWERTY layout.
+ *
+ * If not specified, the virtual keyboard will be created with {@link #DEFAULT_LAYOUT_TYPE}.
+ */
+ @NonNull
+ public Builder setLayoutType(@NonNull String layoutType) {
+ Objects.requireNonNull(layoutType, "layoutType cannot be null");
+ mLayoutType = layoutType;
+ return this;
+ }
+
/**
* Builds the {@link VirtualKeyboardConfig} instance.
*/
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index c3dba33ab881..38b3174abd4c 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -353,6 +353,11 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork
return mCapabilitiesMatchCriteria.get(NET_CAPABILITY_RCS);
}
+ /** @hide */
+ public Map<Integer, Integer> getCapabilitiesMatchCriteria() {
+ return Collections.unmodifiableMap(new HashMap<>(mCapabilitiesMatchCriteria));
+ }
+
@Override
public int hashCode() {
return Objects.hash(
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 3a7aea5d5194..70cf97308ffb 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -114,13 +114,28 @@ public class VcnManager {
public static final String VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY =
"vcn_restricted_transports";
+ /**
+ * Key for maximum number of parallel SAs for tunnel aggregation
+ *
+ * <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be
+ * aggregated over the various tunnels.
+ *
+ * <p>Defaults to 1, unless overridden by carrier config
+ *
+ * @hide
+ */
+ @NonNull
+ public static final String VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY =
+ "vcn_tunnel_aggregation_sa_count_max";
+
/** List of Carrier Config options to extract from Carrier Config bundles. @hide */
@NonNull
public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS =
new String[] {
VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
- VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY
+ VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
+ VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY,
};
private static final Map<
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index 86135bcb0abf..85d6d83ef9e9 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import com.android.internal.util.Preconditions;
@@ -113,24 +114,36 @@ public final class PerformanceHintManager {
* This hint indicates a sudden increase in CPU workload intensity. It means
* that this hint session needs extra CPU resources immediately to meet the
* target duration for the current work cycle.
+ *
+ * @hide
*/
+ @TestApi
public static final int CPU_LOAD_UP = 0;
/**
* This hint indicates a decrease in CPU workload intensity. It means that
* this hint session can reduce CPU resources and still meet the target duration.
+ *
+ * @hide
*/
+ @TestApi
public static final int CPU_LOAD_DOWN = 1;
- /*
+ /**
* This hint indicates an upcoming CPU workload that is completely changed and
* unknown. It means that the hint session should reset CPU resources to a known
* baseline to prepare for an arbitrary load, and must wake up if inactive.
+ *
+ * @hide
*/
+ @TestApi
public static final int CPU_LOAD_RESET = 2;
- /*
+ /**
* This hint indicates that the most recent CPU workload is resuming after a
* period of inactivity. It means that the hint session should allocate similar
* CPU resources to what was used previously, and must wake up if inactive.
+ *
+ * @hide
*/
+ @TestApi
public static final int CPU_LOAD_RESUME = 3;
/** @hide */
@@ -196,7 +209,10 @@ public final class PerformanceHintManager {
* Sends performance hints to inform the hint session of changes in the workload.
*
* @param hint The hint to send to the session.
+ *
+ * @hide
*/
+ @TestApi
public void sendHint(@Hint int hint) {
Preconditions.checkArgumentNonNegative(hint, "the hint ID should be at least"
+ " zero.");
diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java
index 42dd52840575..878df4baf1f8 100644
--- a/core/java/android/service/credentials/Action.java
+++ b/core/java/android/service/credentials/Action.java
@@ -42,7 +42,7 @@ public final class Action implements Parcelable {
* level authentication before displaying any content etc.
*
* <p> See details on usage of {@code Action} for various actionable entries in
- * {@link BeginCreateCredentialResponse} and {@link BeginGetCredentialsResponse}.
+ * {@link BeginCreateCredentialResponse} and {@link BeginGetCredentialResponse}.
*
* @param slice the display content to be displayed on the UI, along with this action
* @param pendingIntent the intent to be invoked when the user selects this action
diff --git a/core/java/android/service/credentials/BeginCreateCredentialRequest.java b/core/java/android/service/credentials/BeginCreateCredentialRequest.java
index 1918d8c88ccc..d976813de780 100644
--- a/core/java/android/service/credentials/BeginCreateCredentialRequest.java
+++ b/core/java/android/service/credentials/BeginCreateCredentialRequest.java
@@ -31,28 +31,28 @@ import java.util.Objects;
* See {@link BeginCreateCredentialResponse} for the counterpart response
*/
public final class BeginCreateCredentialRequest implements Parcelable {
- private final @NonNull String mCallingPackage;
+ private final @NonNull CallingAppInfo mCallingAppInfo;
private final @NonNull String mType;
private final @NonNull Bundle mData;
/**
* Constructs a new instance.
*
- * @throws IllegalArgumentException If {@code callingPackage}, or {@code type} string is
+ * @throws IllegalArgumentException If {@code callingAppInfo}, or {@code type} string is
* null or empty.
* @throws NullPointerException If {@code data} is null.
*/
- public BeginCreateCredentialRequest(@NonNull String callingPackage,
+ public BeginCreateCredentialRequest(@NonNull CallingAppInfo callingAppInfo,
@NonNull String type, @NonNull Bundle data) {
- mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage,
- "callingPackage must not be null or empty");
+ mCallingAppInfo = Objects.requireNonNull(callingAppInfo,
+ "callingAppInfo must not be null");
mType = Preconditions.checkStringNotEmpty(type,
"type must not be null or empty");
mData = Objects.requireNonNull(data, "data must not be null");
}
private BeginCreateCredentialRequest(@NonNull Parcel in) {
- mCallingPackage = in.readString8();
+ mCallingAppInfo = in.readTypedObject(CallingAppInfo.CREATOR);
mType = in.readString8();
mData = in.readBundle(Bundle.class.getClassLoader());
}
@@ -77,15 +77,15 @@ public final class BeginCreateCredentialRequest implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mCallingPackage);
+ dest.writeTypedObject(mCallingAppInfo, flags);
dest.writeString8(mType);
dest.writeBundle(mData);
}
- /** Returns the calling package of the calling app. */
+ /** Returns the info pertaining to the calling app. */
@NonNull
- public String getCallingPackage() {
- return mCallingPackage;
+ public CallingAppInfo getCallingAppInfo() {
+ return mCallingAppInfo;
}
/** Returns the type of the credential to be created. */
diff --git a/core/java/android/service/credentials/BeginGetCredentialOption.java b/core/java/android/service/credentials/BeginGetCredentialOption.java
index c82b445d19e0..74ac6c4ab8c2 100644
--- a/core/java/android/service/credentials/BeginGetCredentialOption.java
+++ b/core/java/android/service/credentials/BeginGetCredentialOption.java
@@ -29,7 +29,7 @@ import com.android.internal.util.Preconditions;
/**
* A specific type of credential request to be sent to the provider during the query phase of
* a get flow. This request contains limited parameters needed to populate a list of
- * {@link CredentialEntry} on the {@link BeginGetCredentialsResponse}.
+ * {@link CredentialEntry} on the {@link BeginGetCredentialResponse}.
*/
public final class BeginGetCredentialOption implements Parcelable {
@@ -56,7 +56,7 @@ public final class BeginGetCredentialOption implements Parcelable {
/**
* Returns the request candidate query data, denoting a set of parameters
* that can be used to populate a candidate list of credentials, as
- * {@link CredentialEntry} on {@link BeginGetCredentialsResponse}. This list
+ * {@link CredentialEntry} on {@link BeginGetCredentialResponse}. This list
* of entries is then presented to the user on a selector.
*
* <p>This data does not contain any sensitive parameters, and will be sent
diff --git a/core/java/android/service/credentials/BeginGetCredentialsRequest.aidl b/core/java/android/service/credentials/BeginGetCredentialRequest.aidl
index 5e1fe8abc2aa..9fb54b074135 100644
--- a/core/java/android/service/credentials/BeginGetCredentialsRequest.aidl
+++ b/core/java/android/service/credentials/BeginGetCredentialRequest.aidl
@@ -1,3 +1,3 @@
package android.service.credentials;
-parcelable BeginGetCredentialsRequest; \ No newline at end of file
+parcelable BeginGetCredentialRequest; \ No newline at end of file
diff --git a/core/java/android/service/credentials/BeginGetCredentialsRequest.java b/core/java/android/service/credentials/BeginGetCredentialRequest.java
index 795840b42f2a..e375cdd56b08 100644
--- a/core/java/android/service/credentials/BeginGetCredentialsRequest.java
+++ b/core/java/android/service/credentials/BeginGetCredentialRequest.java
@@ -35,16 +35,16 @@ import java.util.Objects;
*
* <p>This request contains a list of {@link GetCredentialOption} that have parameters
* to be used to query credentials, and return a list of {@link CredentialEntry} to be set
- * on the {@link BeginGetCredentialsResponse}. This list is then shown to the user on a selector.
+ * on the {@link BeginGetCredentialResponse}. This list is then shown to the user on a selector.
*
* If a {@link PendingIntent} is set on a {@link CredentialEntry}, and the user selects that
* entry, a {@link GetCredentialRequest} with all parameters needed to get the actual
* {@link android.credentials.Credential} will be sent as part of the {@link Intent} fired
* through the {@link PendingIntent}.
*/
-public final class BeginGetCredentialsRequest implements Parcelable {
- /** Calling package of the app requesting for credentials. */
- @NonNull private final String mCallingPackage;
+public final class BeginGetCredentialRequest implements Parcelable {
+ /** Info pertaining to the app requesting for credentials. */
+ @NonNull private final CallingAppInfo mCallingAppInfo;
/**
* List of credential options. Each {@link BeginGetCredentialOption} object holds parameters to
@@ -52,35 +52,36 @@ public final class BeginGetCredentialsRequest implements Parcelable {
*
* This request does not reveal sensitive parameters. Complete list of parameters
* is retrieved through the {@link PendingIntent} set on each {@link CredentialEntry}
- * on {@link CredentialsResponseContent} set on {@link BeginGetCredentialsResponse},
+ * on {@link CredentialsResponseContent} set on {@link BeginGetCredentialResponse},
* when the user selects one of these entries.
*/
@NonNull private final List<BeginGetCredentialOption> mBeginGetCredentialOptions;
- private BeginGetCredentialsRequest(@NonNull String callingPackage,
+ private BeginGetCredentialRequest(@NonNull CallingAppInfo callingAppInfo,
@NonNull List<BeginGetCredentialOption> getBeginCredentialOptions) {
- this.mCallingPackage = callingPackage;
+ this.mCallingAppInfo = callingAppInfo;
this.mBeginGetCredentialOptions = getBeginCredentialOptions;
}
- private BeginGetCredentialsRequest(@NonNull Parcel in) {
- mCallingPackage = in.readString8();
+ private BeginGetCredentialRequest(@NonNull Parcel in) {
+ mCallingAppInfo = in.readTypedObject(CallingAppInfo.CREATOR);
List<BeginGetCredentialOption> getBeginCredentialOptions = new ArrayList<>();
in.readTypedList(getBeginCredentialOptions, BeginGetCredentialOption.CREATOR);
mBeginGetCredentialOptions = getBeginCredentialOptions;
AnnotationValidations.validate(NonNull.class, null, mBeginGetCredentialOptions);
}
- public static final @NonNull Creator<BeginGetCredentialsRequest> CREATOR =
- new Creator<BeginGetCredentialsRequest>() {
+ @NonNull
+ public static final Creator<BeginGetCredentialRequest> CREATOR =
+ new Creator<BeginGetCredentialRequest>() {
@Override
- public BeginGetCredentialsRequest createFromParcel(Parcel in) {
- return new BeginGetCredentialsRequest(in);
+ public BeginGetCredentialRequest createFromParcel(Parcel in) {
+ return new BeginGetCredentialRequest(in);
}
@Override
- public BeginGetCredentialsRequest[] newArray(int size) {
- return new BeginGetCredentialsRequest[size];
+ public BeginGetCredentialRequest[] newArray(int size) {
+ return new BeginGetCredentialRequest[size];
}
};
@@ -91,40 +92,40 @@ public final class BeginGetCredentialsRequest implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mCallingPackage);
+ dest.writeTypedObject(mCallingAppInfo, flags);
dest.writeTypedList(mBeginGetCredentialOptions);
}
/**
- * Returns the calling package of the app requesting credentials.
+ * Returns info pertaining to the app requesting credentials.
*/
- public @NonNull String getCallingPackage() {
- return mCallingPackage;
+ public @NonNull CallingAppInfo getCallingAppInfo() {
+ return mCallingAppInfo;
}
/**
* Returns the list of type specific credential options to list credentials for in
- * {@link BeginGetCredentialsResponse}.
+ * {@link BeginGetCredentialResponse}.
*/
public @NonNull List<BeginGetCredentialOption> getBeginGetCredentialOptions() {
return mBeginGetCredentialOptions;
}
/**
- * Builder for {@link BeginGetCredentialsRequest}.
+ * Builder for {@link BeginGetCredentialRequest}.
*/
public static final class Builder {
- private String mCallingPackage;
+ private CallingAppInfo mCallingAppInfo;
private List<BeginGetCredentialOption> mBeginGetCredentialOptions = new ArrayList<>();
/**
* Creates a new builder.
- * @param callingPackage the calling package of the app requesting credentials
+ * @param callingAppInfo info pertaining to the app requesting credentials
*
- * @throws IllegalArgumentException If {@code callingPackage} is null or empty.
+ * @throws IllegalArgumentException If {@code callingAppInfo} is null or empty.
*/
- public Builder(@NonNull String callingPackage) {
- mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage);
+ public Builder(@NonNull CallingAppInfo callingAppInfo) {
+ mCallingAppInfo = Objects.requireNonNull(callingAppInfo);
}
/**
@@ -158,18 +159,17 @@ public final class BeginGetCredentialsRequest implements Parcelable {
}
/**
- * Builds a new {@link BeginGetCredentialsRequest} instance.
+ * Builds a new {@link BeginGetCredentialRequest} instance.
*
* @throws NullPointerException If {@code beginGetCredentialOptions} is null.
* @throws IllegalArgumentException If {@code beginGetCredentialOptions} is empty, or if
- * {@code callingPackage} is null or empty.
+ * {@code callingAppInfo} is null or empty.
*/
- public @NonNull BeginGetCredentialsRequest build() {
- Preconditions.checkStringNotEmpty(mCallingPackage,
- "Must set the calling package");
+ public @NonNull BeginGetCredentialRequest build() {
+ Objects.requireNonNull(mCallingAppInfo, "callingAppInfo");
Preconditions.checkCollectionNotEmpty(mBeginGetCredentialOptions,
"beginGetCredentialOptions");
- return new BeginGetCredentialsRequest(mCallingPackage, mBeginGetCredentialOptions);
+ return new BeginGetCredentialRequest(mCallingAppInfo, mBeginGetCredentialOptions);
}
}
}
diff --git a/core/java/android/service/credentials/BeginGetCredentialResponse.aidl b/core/java/android/service/credentials/BeginGetCredentialResponse.aidl
new file mode 100644
index 000000000000..d73a747db38a
--- /dev/null
+++ b/core/java/android/service/credentials/BeginGetCredentialResponse.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable BeginGetCredentialResponse; \ No newline at end of file
diff --git a/core/java/android/service/credentials/BeginGetCredentialsResponse.java b/core/java/android/service/credentials/BeginGetCredentialResponse.java
index 2cda56067ba8..85e8d85036fd 100644
--- a/core/java/android/service/credentials/BeginGetCredentialsResponse.java
+++ b/core/java/android/service/credentials/BeginGetCredentialResponse.java
@@ -27,7 +27,7 @@ import java.util.Objects;
* Response from a credential provider, containing credential entries and other associated
* data to be shown on the account selector UI.
*/
-public final class BeginGetCredentialsResponse implements Parcelable {
+public final class BeginGetCredentialResponse implements Parcelable {
/** Content to be used for the UI. */
private final @Nullable CredentialsResponseContent mCredentialsResponseContent;
@@ -38,7 +38,7 @@ public final class BeginGetCredentialsResponse implements Parcelable {
private final @Nullable Action mAuthenticationAction;
/**
- * Creates a {@link BeginGetCredentialsResponse} instance with an authentication
+ * Creates a {@link BeginGetCredentialResponse} instance with an authentication
* {@link Action} set. Providers must use this method when no content can be shown
* before authentication.
*
@@ -55,49 +55,49 @@ public final class BeginGetCredentialsResponse implements Parcelable {
*
* @throws NullPointerException If {@code authenticationAction} is null.
*/
- public static @NonNull BeginGetCredentialsResponse createWithAuthentication(
+ public static @NonNull BeginGetCredentialResponse createWithAuthentication(
@NonNull Action authenticationAction) {
Objects.requireNonNull(authenticationAction,
"authenticationAction must not be null");
- return new BeginGetCredentialsResponse(null, authenticationAction);
+ return new BeginGetCredentialResponse(null, authenticationAction);
}
/**
- * Creates a {@link BeginGetCredentialsRequest} instance with content to be shown on the UI.
+ * Creates a {@link BeginGetCredentialRequest} instance with content to be shown on the UI.
* Providers must use this method when there is content to be shown without top level
* authentication required, including credential entries, action entries or a remote entry,
*
* @throws NullPointerException If {@code credentialsResponseContent} is null.
*/
- public static @NonNull BeginGetCredentialsResponse createWithResponseContent(
+ public static @NonNull BeginGetCredentialResponse createWithResponseContent(
@NonNull CredentialsResponseContent credentialsResponseContent) {
Objects.requireNonNull(credentialsResponseContent,
"credentialsResponseContent must not be null");
- return new BeginGetCredentialsResponse(credentialsResponseContent, null);
+ return new BeginGetCredentialResponse(credentialsResponseContent, null);
}
- private BeginGetCredentialsResponse(@Nullable CredentialsResponseContent
+ private BeginGetCredentialResponse(@Nullable CredentialsResponseContent
credentialsResponseContent,
@Nullable Action authenticationAction) {
mCredentialsResponseContent = credentialsResponseContent;
mAuthenticationAction = authenticationAction;
}
- private BeginGetCredentialsResponse(@NonNull Parcel in) {
+ private BeginGetCredentialResponse(@NonNull Parcel in) {
mCredentialsResponseContent = in.readTypedObject(CredentialsResponseContent.CREATOR);
mAuthenticationAction = in.readTypedObject(Action.CREATOR);
}
- public static final @NonNull Creator<BeginGetCredentialsResponse> CREATOR =
- new Creator<BeginGetCredentialsResponse>() {
+ public static final @NonNull Creator<BeginGetCredentialResponse> CREATOR =
+ new Creator<BeginGetCredentialResponse>() {
@Override
- public BeginGetCredentialsResponse createFromParcel(Parcel in) {
- return new BeginGetCredentialsResponse(in);
+ public BeginGetCredentialResponse createFromParcel(Parcel in) {
+ return new BeginGetCredentialResponse(in);
}
@Override
- public BeginGetCredentialsResponse[] newArray(int size) {
- return new BeginGetCredentialsResponse[size];
+ public BeginGetCredentialResponse[] newArray(int size) {
+ return new BeginGetCredentialResponse[size];
}
};
diff --git a/core/java/android/service/credentials/BeginGetCredentialsResponse.aidl b/core/java/android/service/credentials/BeginGetCredentialsResponse.aidl
deleted file mode 100644
index ca69bcaa866b..000000000000
--- a/core/java/android/service/credentials/BeginGetCredentialsResponse.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.service.credentials;
-
-parcelable BeginGetCredentialsResponse; \ No newline at end of file
diff --git a/core/java/android/service/credentials/CallingAppInfo.java b/core/java/android/service/credentials/CallingAppInfo.java
new file mode 100644
index 000000000000..6116726959ba
--- /dev/null
+++ b/core/java/android/service/credentials/CallingAppInfo.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.annotation.NonNull;
+import android.content.pm.Signature;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Information pertaining to the calling application, including the package name and a list of
+ * app signatures.
+ */
+public final class CallingAppInfo implements Parcelable {
+ @NonNull private final String mPackageName;
+ @NonNull private final Set<Signature> mSignatures;
+
+ /**
+ * Constructs a new instance.
+ *
+ * @throws IllegalArgumentException If {@code packageName} is null or empty.
+ * @throws NullPointerException If {@code signatures} is null.
+ */
+ public CallingAppInfo(@NonNull String packageName,
+ @NonNull Set<Signature> signatures) {
+ mPackageName = Preconditions.checkStringNotEmpty(packageName,
+ "packageName must not be null or empty");
+ mSignatures = Objects.requireNonNull(signatures);
+ }
+
+ private CallingAppInfo(@NonNull Parcel in) {
+ final ClassLoader boot = Object.class.getClassLoader();
+ mPackageName = in.readString8();
+ ArraySet<Signature> signatures = (ArraySet<Signature>) in.readArraySet(boot);
+ mSignatures = signatures == null ? new ArraySet<>() : signatures;
+ }
+
+ public static final @NonNull Creator<CallingAppInfo> CREATOR = new Creator<CallingAppInfo>() {
+ @Override
+ public CallingAppInfo createFromParcel(Parcel in) {
+ return new CallingAppInfo(in);
+ }
+
+ @Override
+ public CallingAppInfo[] newArray(int size) {
+ return new CallingAppInfo[size];
+ }
+ };
+
+ /** Returns the package name of the source of this info. */
+ @NonNull public String getPackageName() {
+ return mPackageName;
+ }
+
+ /** Returns the Set of signatures belonging to the app */
+ @NonNull public Set<Signature> getSignatures() {
+ return mSignatures;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mPackageName);
+ dest.writeArraySet(new ArraySet<>(mSignatures));
+ }
+
+ @Override
+ public String toString() {
+ return "CallingAppInfo {"
+ + "packageName= " + mPackageName
+ + ", No. of signatures: " + mSignatures.size()
+ + " }";
+ }
+}
diff --git a/core/java/android/service/credentials/CreateCredentialRequest.java b/core/java/android/service/credentials/CreateCredentialRequest.java
index aee85abcc8b8..603f95ce3b66 100644
--- a/core/java/android/service/credentials/CreateCredentialRequest.java
+++ b/core/java/android/service/credentials/CreateCredentialRequest.java
@@ -29,28 +29,28 @@ import java.util.Objects;
* Request for creating a credential.
*/
public final class CreateCredentialRequest implements Parcelable {
- private final @NonNull String mCallingPackage;
+ private final @NonNull CallingAppInfo mCallingAppInfo;
private final @NonNull String mType;
private final @NonNull Bundle mData;
/**
* Constructs a new instance.
*
- * @throws IllegalArgumentException If {@code callingPackage}, or {@code type} string is
+ * @throws IllegalArgumentException If {@code callingAppInfo}, or {@code type} string is
* null or empty.
* @throws NullPointerException If {@code data} is null.
*/
- public CreateCredentialRequest(@NonNull String callingPackage,
+ public CreateCredentialRequest(@NonNull CallingAppInfo callingAppInfo,
@NonNull String type, @NonNull Bundle data) {
- mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage,
- "callingPackage must not be null or empty");
+ mCallingAppInfo = Objects.requireNonNull(callingAppInfo,
+ "callingAppInfo must not be null");
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();
+ mCallingAppInfo = in.readTypedObject(CallingAppInfo.CREATOR);
mType = in.readString8();
mData = in.readTypedObject(Bundle.CREATOR);
}
@@ -75,15 +75,15 @@ public final class CreateCredentialRequest implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mCallingPackage);
+ dest.writeTypedObject(mCallingAppInfo, flags);
dest.writeString8(mType);
dest.writeTypedObject(mData, flags);
}
- /** Returns the calling package of the calling app. */
+ /** Returns info pertaining to the calling app. */
@NonNull
- public String getCallingPackage() {
- return mCallingPackage;
+ public CallingAppInfo getCallingAppInfo() {
+ return mCallingAppInfo;
}
/** Returns the type of the credential to be created. */
diff --git a/core/java/android/service/credentials/CredentialProviderException.java b/core/java/android/service/credentials/CredentialProviderException.java
index 02b74430493c..969bcb57ff90 100644
--- a/core/java/android/service/credentials/CredentialProviderException.java
+++ b/core/java/android/service/credentials/CredentialProviderException.java
@@ -24,6 +24,8 @@ import java.lang.annotation.RetentionPolicy;
/**
* Contains custom exceptions to be used by credential providers on failure.
+ *
+ * @hide
*/
public class CredentialProviderException extends Exception {
public static final int ERROR_UNKNOWN = 0;
@@ -44,6 +46,14 @@ public class CredentialProviderException extends Exception {
*/
public static final int ERROR_TASK_CANCELED = 2;
+ /**
+ * For internal use only.
+ * Error code to be used when the provider encounters a failure while processing the request.
+ *
+ * @hide
+ */
+ public static final int ERROR_PROVIDER_FAILURE = 3;
+
private final int mErrorCode;
/**
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index 416ddf172616..7735e4cdb394 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -24,6 +24,8 @@ import android.annotation.SdkConstant;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
+import android.credentials.CreateCredentialException;
+import android.credentials.GetCredentialException;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
@@ -54,7 +56,7 @@ public abstract class CredentialProviderService extends Service {
/**
* Intent extra: The {@link GetCredentialRequest} attached with
* the {@code pendingIntent} that is invoked when the user selects a {@link CredentialEntry}
- * returned as part of the {@link BeginGetCredentialsResponse}
+ * returned as part of the {@link BeginGetCredentialResponse}
*
* <p>
* Type: {@link GetCredentialRequest}
@@ -87,7 +89,7 @@ public abstract class CredentialProviderService extends Service {
/**
* Intent extra: The result of an authentication flow, to be set on finish of the
* {@link android.app.Activity} invoked through the {@link android.app.PendingIntent} set on
- * a {@link BeginGetCredentialsResponse}. This result should contain the actual content,
+ * a {@link BeginGetCredentialResponse}. This result should contain the actual content,
* including credential entries and action entries, to be shown on the selector.
*
* <p>
@@ -154,22 +156,21 @@ public abstract class CredentialProviderService extends Service {
}
private final ICredentialProviderService mInterface = new ICredentialProviderService.Stub() {
- @Override
- public ICancellationSignal onBeginGetCredentials(BeginGetCredentialsRequest request,
- IBeginGetCredentialsCallback callback) {
+ public ICancellationSignal onBeginGetCredential(BeginGetCredentialRequest request,
+ IBeginGetCredentialCallback callback) {
Objects.requireNonNull(request);
Objects.requireNonNull(callback);
ICancellationSignal transport = CancellationSignal.createTransport();
mHandler.sendMessage(obtainMessage(
- CredentialProviderService::onBeginGetCredentials,
+ CredentialProviderService::onBeginGetCredential,
CredentialProviderService.this, request,
CancellationSignal.fromTransport(transport),
- new OutcomeReceiver<BeginGetCredentialsResponse,
- CredentialProviderException>() {
+ new OutcomeReceiver<BeginGetCredentialResponse,
+ GetCredentialException>() {
@Override
- public void onResult(BeginGetCredentialsResponse result) {
+ public void onResult(BeginGetCredentialResponse result) {
try {
callback.onSuccess(result);
} catch (RemoteException e) {
@@ -177,9 +178,9 @@ public abstract class CredentialProviderService extends Service {
}
}
@Override
- public void onError(CredentialProviderException e) {
+ public void onError(GetCredentialException e) {
try {
- callback.onFailure(e.getErrorCode(), e.getMessage());
+ callback.onFailure(e.errorType, e.getMessage());
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
@@ -202,7 +203,7 @@ public abstract class CredentialProviderService extends Service {
CredentialProviderService.this, request,
CancellationSignal.fromTransport(transport),
new OutcomeReceiver<
- BeginCreateCredentialResponse, CredentialProviderException>() {
+ BeginCreateCredentialResponse, CreateCredentialException>() {
@Override
public void onResult(BeginCreateCredentialResponse result) {
try {
@@ -212,9 +213,9 @@ public abstract class CredentialProviderService extends Service {
}
}
@Override
- public void onError(CredentialProviderException e) {
+ public void onError(CreateCredentialException e) {
try {
- callback.onFailure(e.getErrorCode(), e.getMessage());
+ callback.onFailure(e.errorType, e.getMessage());
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
@@ -229,13 +230,11 @@ public abstract class CredentialProviderService extends Service {
* Called by the android system to retrieve user credentials from the connected provider
* service.
*
- *
- *
* <p>This API denotes a query stage request for getting user's credentials from a given
* credential provider. The request contains a list of
* {@link android.credentials.GetCredentialOption} that have parameters to be used for
* populating candidate credentials, as a list of {@link CredentialEntry} to be set
- * on the {@link BeginGetCredentialsResponse}. This list is then shown to the user on a
+ * on the {@link BeginGetCredentialResponse}. This list is then shown to the user on a
* selector.
*
* <p>If a {@link PendingIntent} is set on a {@link CredentialEntry}, and the user selects that
@@ -247,10 +246,10 @@ public abstract class CredentialProviderService extends Service {
* the android system
* @param callback object used to relay the response of the credentials request
*/
- public abstract void onBeginGetCredentials(@NonNull BeginGetCredentialsRequest request,
+ public abstract void onBeginGetCredential(@NonNull BeginGetCredentialRequest request,
@NonNull CancellationSignal cancellationSignal,
@NonNull OutcomeReceiver<
- BeginGetCredentialsResponse, CredentialProviderException> callback);
+ BeginGetCredentialResponse, GetCredentialException> callback);
/**
* Called by the android system to create a credential.
@@ -262,5 +261,5 @@ public abstract class CredentialProviderService extends Service {
public abstract void onBeginCreateCredential(@NonNull BeginCreateCredentialRequest request,
@NonNull CancellationSignal cancellationSignal,
@NonNull OutcomeReceiver<BeginCreateCredentialResponse,
- CredentialProviderException> callback);
+ CreateCredentialException> callback);
}
diff --git a/core/java/android/service/credentials/CredentialsResponseContent.java b/core/java/android/service/credentials/CredentialsResponseContent.java
index c2f28cb1204c..ce6972df88e1 100644
--- a/core/java/android/service/credentials/CredentialsResponseContent.java
+++ b/core/java/android/service/credentials/CredentialsResponseContent.java
@@ -29,7 +29,7 @@ import java.util.Objects;
/**
* The content to be displayed on the account selector UI, including credential entries,
- * actions etc. Returned as part of {@link BeginGetCredentialsResponse}
+ * actions etc. Returned as part of {@link BeginGetCredentialResponse}
*/
public final class CredentialsResponseContent implements Parcelable {
/** List of credential entries to be displayed on the UI. */
diff --git a/core/java/android/service/credentials/GetCredentialRequest.java b/core/java/android/service/credentials/GetCredentialRequest.java
index 1d6c83be0db1..5532b55d09ad 100644
--- a/core/java/android/service/credentials/GetCredentialRequest.java
+++ b/core/java/android/service/credentials/GetCredentialRequest.java
@@ -33,7 +33,7 @@ import java.util.Objects;
*/
public final class GetCredentialRequest implements Parcelable {
/** Calling package of the app requesting for credentials. */
- private final @NonNull String mCallingPackage;
+ private final @NonNull CallingAppInfo mCallingAppInfo;
/**
* List of credential options. Each {@link GetCredentialOption} object holds parameters to
@@ -41,14 +41,14 @@ public final class GetCredentialRequest implements Parcelable {
*/
private final @NonNull List<GetCredentialOption> mGetCredentialOptions;
- private GetCredentialRequest(@NonNull String callingPackage,
+ private GetCredentialRequest(@NonNull CallingAppInfo callingAppInfo,
@NonNull List<GetCredentialOption> getCredentialOptions) {
- this.mCallingPackage = callingPackage;
+ this.mCallingAppInfo = callingAppInfo;
this.mGetCredentialOptions = getCredentialOptions;
}
private GetCredentialRequest(@NonNull Parcel in) {
- mCallingPackage = in.readString8();
+ mCallingAppInfo = in.readTypedObject(CallingAppInfo.CREATOR);
List<GetCredentialOption> getCredentialOptions = new ArrayList<>();
in.readTypedList(getCredentialOptions, GetCredentialOption.CREATOR);
mGetCredentialOptions = getCredentialOptions;
@@ -75,15 +75,15 @@ public final class GetCredentialRequest implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mCallingPackage);
+ dest.writeTypedObject(mCallingAppInfo, flags);
dest.writeTypedList(mGetCredentialOptions);
}
/**
- * Returns the calling package of the app requesting credentials.
+ * Returns info pertaining to the app requesting credentials.
*/
- public @NonNull String getCallingPackage() {
- return mCallingPackage;
+ public @NonNull CallingAppInfo getCallingAppInfo() {
+ return mCallingAppInfo;
}
/**
@@ -97,17 +97,17 @@ public final class GetCredentialRequest implements Parcelable {
* Builder for {@link GetCredentialRequest}.
*/
public static final class Builder {
- private String mCallingPackage;
+ private CallingAppInfo mCallingAppInfo;
private List<GetCredentialOption> mGetCredentialOptions = new ArrayList<>();
/**
* Creates a new builder.
- * @param callingPackage the calling package of the app requesting credentials
+ * @param callingAppInfo info pertaining to the app requesting credentials
*
* @throws IllegalArgumentException If {@code callingPackag}e is null or empty.
*/
- public Builder(@NonNull String callingPackage) {
- mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage);
+ public Builder(@NonNull CallingAppInfo callingAppInfo) {
+ mCallingAppInfo = Objects.requireNonNull(callingAppInfo);
}
/**
@@ -145,14 +145,14 @@ public final class GetCredentialRequest implements Parcelable {
*
* @throws NullPointerException If {@code getCredentialOptions} is null.
* @throws IllegalArgumentException If {@code getCredentialOptions} is empty, or if
- * {@code callingPackage} is null or empty.
+ * {@code callingAppInfo} is null or empty.
*/
public @NonNull GetCredentialRequest build() {
- Preconditions.checkStringNotEmpty(mCallingPackage,
- "Must set the calling package");
+ Objects.requireNonNull(mCallingAppInfo,
+ "mCallingAppInfo");
Preconditions.checkCollectionNotEmpty(mGetCredentialOptions,
"getCredentialOptions");
- return new GetCredentialRequest(mCallingPackage, mGetCredentialOptions);
+ return new GetCredentialRequest(mCallingAppInfo, mGetCredentialOptions);
}
}
}
diff --git a/core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl b/core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl
index ec0bc364a457..ab855ef0b13f 100644
--- a/core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl
+++ b/core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl
@@ -9,5 +9,5 @@ import android.service.credentials.BeginCreateCredentialResponse;
*/
oneway interface IBeginCreateCredentialCallback {
void onSuccess(in BeginCreateCredentialResponse request);
- void onFailure(int errorCode, in CharSequence message);
+ void onFailure(String errorType, in CharSequence message);
} \ No newline at end of file
diff --git a/core/java/android/service/credentials/IBeginGetCredentialCallback.aidl b/core/java/android/service/credentials/IBeginGetCredentialCallback.aidl
new file mode 100644
index 000000000000..73e98707d15e
--- /dev/null
+++ b/core/java/android/service/credentials/IBeginGetCredentialCallback.aidl
@@ -0,0 +1,13 @@
+package android.service.credentials;
+
+import android.service.credentials.BeginGetCredentialResponse;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface IBeginGetCredentialCallback {
+ void onSuccess(in BeginGetCredentialResponse response);
+ void onFailure(String errorType, in CharSequence message);
+} \ No newline at end of file
diff --git a/core/java/android/service/credentials/IBeginGetCredentialsCallback.aidl b/core/java/android/service/credentials/IBeginGetCredentialsCallback.aidl
deleted file mode 100644
index 9ac28f26059b..000000000000
--- a/core/java/android/service/credentials/IBeginGetCredentialsCallback.aidl
+++ /dev/null
@@ -1,13 +0,0 @@
-package android.service.credentials;
-
-import android.service.credentials.BeginGetCredentialsResponse;
-
-/**
- * Interface from the system to a credential provider service.
- *
- * @hide
- */
-oneway interface IBeginGetCredentialsCallback {
- void onSuccess(in BeginGetCredentialsResponse response);
- 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
index 130688291795..24d253fa61ef 100644
--- a/core/java/android/service/credentials/ICredentialProviderService.aidl
+++ b/core/java/android/service/credentials/ICredentialProviderService.aidl
@@ -17,9 +17,9 @@
package android.service.credentials;
import android.os.ICancellationSignal;
-import android.service.credentials.BeginGetCredentialsRequest;
+import android.service.credentials.BeginGetCredentialRequest;
import android.service.credentials.BeginCreateCredentialRequest;
-import android.service.credentials.IBeginGetCredentialsCallback;
+import android.service.credentials.IBeginGetCredentialCallback;
import android.service.credentials.IBeginCreateCredentialCallback;
import android.os.ICancellationSignal;
@@ -29,6 +29,6 @@ import android.os.ICancellationSignal;
* @hide
*/
interface ICredentialProviderService {
- ICancellationSignal onBeginGetCredentials(in BeginGetCredentialsRequest request, in IBeginGetCredentialsCallback callback);
+ ICancellationSignal onBeginGetCredential(in BeginGetCredentialRequest request, in IBeginGetCredentialCallback callback);
ICancellationSignal onBeginCreateCredential(in BeginCreateCredentialRequest request, in IBeginCreateCredentialCallback callback);
}
diff --git a/core/java/android/service/voice/HotwordAudioStream.java b/core/java/android/service/voice/HotwordAudioStream.java
index 6ae952c045cb..a5a0b53f48d7 100644
--- a/core/java/android/service/voice/HotwordAudioStream.java
+++ b/core/java/android/service/voice/HotwordAudioStream.java
@@ -29,6 +29,7 @@ import android.os.PersistableBundle;
import com.android.internal.util.DataClass;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -68,8 +69,17 @@ public final class HotwordAudioStream implements Parcelable {
private final AudioFormat mAudioFormat;
/**
- * This stream starts with the audio bytes used for hotword detection, but continues streaming
- * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ * This stream typically starts with the audio bytes used for hotword detection, but continues
+ * streaming the audio (e.g., with the query) until the stream is shutdown by the
+ * {@link HotwordDetectionService}. The data format is expected to match
+ * {@link #getAudioFormat()}.
+ *
+ * <p>
+ * Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()}
+ * to pass the start of the audio instead of streaming it here. This may prevent added latency
+ * caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not
+ * being large enough to handle this initial chunk of audio.
+ * </p>
*/
@NonNull
private final ParcelFileDescriptor mAudioStreamParcelFileDescriptor;
@@ -108,13 +118,62 @@ public final class HotwordAudioStream implements Parcelable {
}
/**
+ * The start of the audio used for hotword detection. The data format is expected to match
+ * {@link #getAudioFormat()}.
+ *
+ * <p>
+ * The {@link HotwordDetectionService} may use this instead of using
+ * {@link #getAudioStreamParcelFileDescriptor()} to stream these initial bytes of audio. This
+ * may prevent added latency caused by the streaming buffer (see
+ * {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not being large enough to handle this
+ * initial chunk of audio.
+ * </p>
+ */
+ @NonNull
+ private final byte[] mInitialAudio;
+ private static final byte[] DEFAULT_INITIAL_EMPTY_AUDIO = {};
+ private static byte[] defaultInitialAudio() {
+ return DEFAULT_INITIAL_EMPTY_AUDIO;
+ }
+
+ private String initialAudioToString() {
+ return "length=" + mInitialAudio.length;
+ }
+
+ /**
* Provides an instance of {@link Builder} with state corresponding to this instance.
* @hide
*/
public Builder buildUpon() {
return new Builder(mAudioFormat, mAudioStreamParcelFileDescriptor)
.setTimestamp(mTimestamp)
- .setMetadata(mMetadata);
+ .setMetadata(mMetadata)
+ .setInitialAudio(mInitialAudio);
+ }
+
+ @DataClass.Suppress("setInitialAudio")
+ abstract static class BaseBuilder {
+
+ /**
+ * The start of the audio used for hotword detection. The data format is expected to match
+ * {@link #getAudioFormat()}.
+ *
+ * <p>
+ * The {@link HotwordDetectionService} may use this instead of using
+ * {@link #getAudioStreamParcelFileDescriptor()} to stream these initial bytes of audio.
+ * This may prevent added latency caused by the streaming buffer (see
+ * {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not being large enough to handle this
+ * initial chunk of audio.
+ * </p>
+ */
+ public @NonNull Builder setInitialAudio(@NonNull byte[] value) {
+ Objects.requireNonNull(value, "value should not be null");
+ final Builder builder = (Builder) this;
+ // If the code gen flag in build() is changed, we must update the flag e.g. 0x10 here.
+ builder.mBuilderFieldsSet |= 0x10;
+ builder.mInitialAudio = value;
+ return builder;
+ }
}
@@ -137,7 +196,8 @@ public final class HotwordAudioStream implements Parcelable {
@NonNull AudioFormat audioFormat,
@NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor,
@Nullable AudioTimestamp timestamp,
- @NonNull PersistableBundle metadata) {
+ @NonNull PersistableBundle metadata,
+ @NonNull byte[] initialAudio) {
this.mAudioFormat = audioFormat;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mAudioFormat);
@@ -148,6 +208,9 @@ public final class HotwordAudioStream implements Parcelable {
this.mMetadata = metadata;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMetadata);
+ this.mInitialAudio = initialAudio;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInitialAudio);
// onConstructed(); // You can define this method to get a callback
}
@@ -161,8 +224,17 @@ public final class HotwordAudioStream implements Parcelable {
}
/**
- * This stream starts with the audio bytes used for hotword detection, but continues streaming
- * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ * This stream typically starts with the audio bytes used for hotword detection, but continues
+ * streaming the audio (e.g., with the query) until the stream is shutdown by the
+ * {@link HotwordDetectionService}. The data format is expected to match
+ * {@link #getAudioFormat()}.
+ *
+ * <p>
+ * Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()}
+ * to pass the start of the audio instead of streaming it here. This may prevent added latency
+ * caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not
+ * being large enough to handle this initial chunk of audio.
+ * </p>
*/
@DataClass.Generated.Member
public @NonNull ParcelFileDescriptor getAudioStreamParcelFileDescriptor() {
@@ -200,6 +272,23 @@ public final class HotwordAudioStream implements Parcelable {
return mMetadata;
}
+ /**
+ * The start of the audio used for hotword detection. The data format is expected to match
+ * {@link #getAudioFormat()}.
+ *
+ * <p>
+ * The {@link HotwordDetectionService} may use this instead of using
+ * {@link #getAudioStreamParcelFileDescriptor()} to stream these initial bytes of audio. This
+ * may prevent added latency caused by the streaming buffer (see
+ * {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not being large enough to handle this
+ * initial chunk of audio.
+ * </p>
+ */
+ @DataClass.Generated.Member
+ public @NonNull byte[] getInitialAudio() {
+ return mInitialAudio;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -210,7 +299,8 @@ public final class HotwordAudioStream implements Parcelable {
"audioFormat = " + mAudioFormat + ", " +
"audioStreamParcelFileDescriptor = " + mAudioStreamParcelFileDescriptor + ", " +
"timestamp = " + mTimestamp + ", " +
- "metadata = " + mMetadata +
+ "metadata = " + mMetadata + ", " +
+ "initialAudio = " + initialAudioToString() +
" }";
}
@@ -230,7 +320,8 @@ public final class HotwordAudioStream implements Parcelable {
&& Objects.equals(mAudioFormat, that.mAudioFormat)
&& Objects.equals(mAudioStreamParcelFileDescriptor, that.mAudioStreamParcelFileDescriptor)
&& Objects.equals(mTimestamp, that.mTimestamp)
- && Objects.equals(mMetadata, that.mMetadata);
+ && Objects.equals(mMetadata, that.mMetadata)
+ && Arrays.equals(mInitialAudio, that.mInitialAudio);
}
@Override
@@ -244,6 +335,7 @@ public final class HotwordAudioStream implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mAudioStreamParcelFileDescriptor);
_hash = 31 * _hash + Objects.hashCode(mTimestamp);
_hash = 31 * _hash + Objects.hashCode(mMetadata);
+ _hash = 31 * _hash + Arrays.hashCode(mInitialAudio);
return _hash;
}
@@ -260,6 +352,7 @@ public final class HotwordAudioStream implements Parcelable {
dest.writeTypedObject(mAudioStreamParcelFileDescriptor, flags);
if (mTimestamp != null) dest.writeTypedObject(mTimestamp, flags);
dest.writeTypedObject(mMetadata, flags);
+ dest.writeByteArray(mInitialAudio);
}
@Override
@@ -278,6 +371,7 @@ public final class HotwordAudioStream implements Parcelable {
ParcelFileDescriptor audioStreamParcelFileDescriptor = (ParcelFileDescriptor) in.readTypedObject(ParcelFileDescriptor.CREATOR);
AudioTimestamp timestamp = (flg & 0x4) == 0 ? null : (AudioTimestamp) in.readTypedObject(AudioTimestamp.CREATOR);
PersistableBundle metadata = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
+ byte[] initialAudio = in.createByteArray();
this.mAudioFormat = audioFormat;
com.android.internal.util.AnnotationValidations.validate(
@@ -289,6 +383,9 @@ public final class HotwordAudioStream implements Parcelable {
this.mMetadata = metadata;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMetadata);
+ this.mInitialAudio = initialAudio;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInitialAudio);
// onConstructed(); // You can define this method to get a callback
}
@@ -312,12 +409,13 @@ public final class HotwordAudioStream implements Parcelable {
*/
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
- public static final class Builder {
+ public static final class Builder extends BaseBuilder {
private @NonNull AudioFormat mAudioFormat;
private @NonNull ParcelFileDescriptor mAudioStreamParcelFileDescriptor;
private @Nullable AudioTimestamp mTimestamp;
private @NonNull PersistableBundle mMetadata;
+ private @NonNull byte[] mInitialAudio;
private long mBuilderFieldsSet = 0L;
@@ -327,8 +425,17 @@ public final class HotwordAudioStream implements Parcelable {
* @param audioFormat
* The {@link AudioFormat} of the audio stream.
* @param audioStreamParcelFileDescriptor
- * This stream starts with the audio bytes used for hotword detection, but continues streaming
- * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ * This stream typically starts with the audio bytes used for hotword detection, but continues
+ * streaming the audio (e.g., with the query) until the stream is shutdown by the
+ * {@link HotwordDetectionService}. The data format is expected to match
+ * {@link #getAudioFormat()}.
+ *
+ * <p>
+ * Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()}
+ * to pass the start of the audio instead of streaming it here. This may prevent added latency
+ * caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not
+ * being large enough to handle this initial chunk of audio.
+ * </p>
*/
public Builder(
@NonNull AudioFormat audioFormat,
@@ -353,8 +460,17 @@ public final class HotwordAudioStream implements Parcelable {
}
/**
- * This stream starts with the audio bytes used for hotword detection, but continues streaming
- * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ * This stream typically starts with the audio bytes used for hotword detection, but continues
+ * streaming the audio (e.g., with the query) until the stream is shutdown by the
+ * {@link HotwordDetectionService}. The data format is expected to match
+ * {@link #getAudioFormat()}.
+ *
+ * <p>
+ * Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()}
+ * to pass the start of the audio instead of streaming it here. This may prevent added latency
+ * caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not
+ * being large enough to handle this initial chunk of audio.
+ * </p>
*/
@DataClass.Generated.Member
public @NonNull Builder setAudioStreamParcelFileDescriptor(@NonNull ParcelFileDescriptor value) {
@@ -404,7 +520,7 @@ public final class HotwordAudioStream implements Parcelable {
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull HotwordAudioStream build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x10; // Mark builder used
+ mBuilderFieldsSet |= 0x20; // Mark builder used
if ((mBuilderFieldsSet & 0x4) == 0) {
mTimestamp = defaultTimestamp();
@@ -412,16 +528,20 @@ public final class HotwordAudioStream implements Parcelable {
if ((mBuilderFieldsSet & 0x8) == 0) {
mMetadata = defaultMetadata();
}
+ if ((mBuilderFieldsSet & 0x10) == 0) {
+ mInitialAudio = defaultInitialAudio();
+ }
HotwordAudioStream o = new HotwordAudioStream(
mAudioFormat,
mAudioStreamParcelFileDescriptor,
mTimestamp,
- mMetadata);
+ mMetadata,
+ mInitialAudio);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x10) != 0) {
+ if ((mBuilderFieldsSet & 0x20) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -429,10 +549,10 @@ public final class HotwordAudioStream implements Parcelable {
}
@DataClass.Generated(
- time = 1669916341034L,
+ time = 1671232056270L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordAudioStream.java",
- inputSignatures = "public static final java.lang.String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static android.media.AudioTimestamp defaultTimestamp()\nprivate static android.os.PersistableBundle defaultMetadata()\npublic android.service.voice.HotwordAudioStream.Builder buildUpon()\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final java.lang.String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate final @android.annotation.NonNull byte[] mInitialAudio\nprivate static final byte[] DEFAULT_INITIAL_EMPTY_AUDIO\nprivate static android.media.AudioTimestamp defaultTimestamp()\nprivate static android.os.PersistableBundle defaultMetadata()\nprivate static byte[] defaultInitialAudio()\nprivate java.lang.String initialAudioToString()\npublic android.service.voice.HotwordAudioStream.Builder buildUpon()\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.NonNull android.service.voice.HotwordAudioStream.Builder setInitialAudio(byte[])\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)\npublic @android.annotation.NonNull android.service.voice.HotwordAudioStream.Builder setInitialAudio(byte[])\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 257f3b7dbf40..e90ae757abd2 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -582,6 +582,14 @@ public class TelephonyCallback {
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37;
+ /**
+ * Event to norify the Anbr information from Radio to Ims.
+ *
+ * @see ImsCallSessionImplBase#callSessionNotifyAnbr.
+ *
+ * @hide
+ */
+ public static final int EVENT_TRIGGER_NOTIFY_ANBR = 38;
/**
* @hide
@@ -623,7 +631,8 @@ public class TelephonyCallback {
EVENT_DATA_ENABLED_CHANGED,
EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED,
EVENT_LEGACY_CALL_STATE_CHANGED,
- EVENT_LINK_CAPACITY_ESTIMATE_CHANGED
+ EVENT_LINK_CAPACITY_ESTIMATE_CHANGED,
+ EVENT_TRIGGER_NOTIFY_ANBR
})
@Retention(RetentionPolicy.SOURCE)
public @interface TelephonyEvent {
diff --git a/core/java/android/text/EmojiConsistency.java b/core/java/android/text/EmojiConsistency.java
new file mode 100644
index 000000000000..dfaa217c0cca
--- /dev/null
+++ b/core/java/android/text/EmojiConsistency.java
@@ -0,0 +1,82 @@
+/*
+ * 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.text;
+
+import android.annotation.NonNull;
+
+import java.util.Collections;
+import java.util.Set;
+
+
+/**
+ * The set of emoji that should be drawn by the system with the default font for device consistency.
+ *
+ * This is intended to be used only by applications that do custom emoji rendering using tools like
+ * {@link android.text.style.ReplacementSpan} or custom emoji fonts.
+ *
+ * An example of how this should be used:
+ *
+ * <p>
+ * <ol>
+ * <li>
+ * Match emoji for third party custom rendering
+ * </li>
+ * <li>
+ * For each match, check against NonStandardEmoji before displaying custom glyph
+ * </li>
+ * <li>
+ * If in NonStandardEmojiSet, do not display custom glyph (render with
+ * android.graphics.Typeface.DEFAULT instead)
+ * </li>
+ * <li>
+ * Otherwise, do custom rendering like normal
+ * </li>
+ * </ol>
+ * </p>
+ */
+public final class EmojiConsistency {
+ /* Cannot construct */
+ private EmojiConsistency() { }
+
+ /**
+ * The set of emoji that should be drawn by the system with the default font for device
+ * consistency.
+ *
+ * Apps SHOULD attempt to avoid overwriting system emoji rendering with custom emoji glyphs for
+ * these codepoint sequences.
+ *
+ * Apps that display custom emoji glyphs via matching code may filter against this set. On
+ * match, the application SHOULD prefer Typeface.Default instead of a custom glyph
+ *
+ * Apps that use fonts may use this set to add {@link android.text.style.TypefaceSpan} for
+ * android.graphics.Typeface.DEFAULT for matched codepoint sequences.
+ *
+ * Codepoint sequences returned MUST match exactly to be considered a match with the exception
+ * of Variation Selectors.
+ *
+ * All codepoint sequences returned MUST be a complete emoji codepoint sequence as defined by
+ * unicode.
+ *
+ * @return set of codepoint sequences representing codepoints that should be rendered by the
+ * system using the default font.
+ */
+ @NonNull
+ public static Set<int[]> getEmojiConsistencySet() {
+ return Collections.emptySet();
+ }
+
+}
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 3d7843c06c6a..89c7a360a91c 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -30,6 +30,7 @@ import android.hardware.input.InputDeviceCountryCode;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.lights.LightsManager;
+import android.icu.util.ULocale;
import android.os.Build;
import android.os.NullVibrator;
import android.os.Parcel;
@@ -76,6 +77,10 @@ public final class InputDevice implements Parcelable {
private final KeyCharacterMap mKeyCharacterMap;
@InputDeviceCountryCode
private final int mCountryCode;
+ @Nullable
+ private final String mKeyboardLanguageTag;
+ @Nullable
+ private final String mKeyboardLayoutType;
private final boolean mHasVibrator;
private final boolean mHasMicrophone;
private final boolean mHasButtonUnderPad;
@@ -464,6 +469,7 @@ public final class InputDevice implements Parcelable {
private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
KeyCharacterMap keyCharacterMap, @InputDeviceCountryCode int countryCode,
+ @Nullable String keyboardLanguageTag, @Nullable String keyboardLayoutType,
boolean hasVibrator, boolean hasMicrophone, boolean hasButtonUnderPad,
boolean hasSensor, boolean hasBattery, boolean supportsUsi) {
mId = id;
@@ -478,6 +484,14 @@ public final class InputDevice implements Parcelable {
mKeyboardType = keyboardType;
mKeyCharacterMap = keyCharacterMap;
mCountryCode = countryCode;
+ if (keyboardLanguageTag != null) {
+ mKeyboardLanguageTag = ULocale
+ .createCanonical(ULocale.forLanguageTag(keyboardLanguageTag))
+ .toLanguageTag();
+ } else {
+ mKeyboardLanguageTag = null;
+ }
+ mKeyboardLayoutType = keyboardLayoutType;
mHasVibrator = hasVibrator;
mHasMicrophone = hasMicrophone;
mHasButtonUnderPad = hasButtonUnderPad;
@@ -500,6 +514,8 @@ public final class InputDevice implements Parcelable {
mSources = in.readInt();
mKeyboardType = in.readInt();
mCountryCode = in.readInt();
+ mKeyboardLanguageTag = in.readString8();
+ mKeyboardLayoutType = in.readString8();
mHasVibrator = in.readInt() != 0;
mHasMicrophone = in.readInt() != 0;
mHasButtonUnderPad = in.readInt() != 0;
@@ -521,6 +537,7 @@ public final class InputDevice implements Parcelable {
/**
* InputDevice builder used to create an InputDevice for tests in Java.
+ *
* @hide
*/
@VisibleForTesting
@@ -543,111 +560,125 @@ public final class InputDevice implements Parcelable {
private boolean mHasBattery = false;
@InputDeviceCountryCode
private int mCountryCode = InputDeviceCountryCode.INVALID;
+ private String mKeyboardLanguageTag = null;
+ private String mKeyboardLayoutType = null;
private boolean mSupportsUsi = false;
- /** @see InputDevice#getId() */
+ /** @see InputDevice#getId() */
public Builder setId(int id) {
mId = id;
return this;
}
- /** @see InputDevice#getGeneration() */
+ /** @see InputDevice#getGeneration() */
public Builder setGeneration(int generation) {
mGeneration = generation;
return this;
}
- /** @see InputDevice#getControllerNumber() */
+ /** @see InputDevice#getControllerNumber() */
public Builder setControllerNumber(int controllerNumber) {
mControllerNumber = controllerNumber;
return this;
}
- /** @see InputDevice#getName() */
+ /** @see InputDevice#getName() */
public Builder setName(String name) {
mName = name;
return this;
}
- /** @see InputDevice#getVendorId() */
+ /** @see InputDevice#getVendorId() */
public Builder setVendorId(int vendorId) {
mVendorId = vendorId;
return this;
}
- /** @see InputDevice#getProductId() */
+ /** @see InputDevice#getProductId() */
public Builder setProductId(int productId) {
mProductId = productId;
return this;
}
- /** @see InputDevice#getDescriptor() */
+ /** @see InputDevice#getDescriptor() */
public Builder setDescriptor(String descriptor) {
mDescriptor = descriptor;
return this;
}
- /** @see InputDevice#isExternal() */
+ /** @see InputDevice#isExternal() */
public Builder setExternal(boolean external) {
mIsExternal = external;
return this;
}
- /** @see InputDevice#getSources() */
+ /** @see InputDevice#getSources() */
public Builder setSources(int sources) {
mSources = sources;
return this;
}
- /** @see InputDevice#getKeyboardType() */
+ /** @see InputDevice#getKeyboardType() */
public Builder setKeyboardType(int keyboardType) {
mKeyboardType = keyboardType;
return this;
}
- /** @see InputDevice#getKeyCharacterMap() */
+ /** @see InputDevice#getKeyCharacterMap() */
public Builder setKeyCharacterMap(KeyCharacterMap keyCharacterMap) {
mKeyCharacterMap = keyCharacterMap;
return this;
}
- /** @see InputDevice#getVibrator() */
+ /** @see InputDevice#getVibrator() */
public Builder setHasVibrator(boolean hasVibrator) {
mHasVibrator = hasVibrator;
return this;
}
- /** @see InputDevice#hasMicrophone() */
+ /** @see InputDevice#hasMicrophone() */
public Builder setHasMicrophone(boolean hasMicrophone) {
mHasMicrophone = hasMicrophone;
return this;
}
- /** @see InputDevice#hasButtonUnderPad() */
+ /** @see InputDevice#hasButtonUnderPad() */
public Builder setHasButtonUnderPad(boolean hasButtonUnderPad) {
mHasButtonUnderPad = hasButtonUnderPad;
return this;
}
- /** @see InputDevice#hasSensor() */
+ /** @see InputDevice#hasSensor() */
public Builder setHasSensor(boolean hasSensor) {
mHasSensor = hasSensor;
return this;
}
- /** @see InputDevice#hasBattery() */
+ /** @see InputDevice#hasBattery() */
public Builder setHasBattery(boolean hasBattery) {
mHasBattery = hasBattery;
return this;
}
- /** @see InputDevice#getCountryCode() */
+ /** @see InputDevice#getCountryCode() */
public Builder setCountryCode(@InputDeviceCountryCode int countryCode) {
mCountryCode = countryCode;
return this;
}
- /** @see InputDevice#supportsUsi() () */
+ /** @see InputDevice#getKeyboardLanguageTag() */
+ public Builder setKeyboardLanguageTag(String keyboardLanguageTag) {
+ mKeyboardLanguageTag = keyboardLanguageTag;
+ return this;
+ }
+
+ /** @see InputDevice#getKeyboardLayoutType() */
+ public Builder setKeyboardLayoutType(String keyboardLayoutType) {
+ mKeyboardLayoutType = keyboardLayoutType;
+ return this;
+ }
+
+ /** @see InputDevice#supportsUsi() () */
public Builder setSupportsUsi(boolean supportsUsi) {
mSupportsUsi = supportsUsi;
return this;
@@ -657,8 +688,8 @@ public final class InputDevice implements Parcelable {
public InputDevice build() {
return new InputDevice(mId, mGeneration, mControllerNumber, mName, mVendorId,
mProductId, mDescriptor, mIsExternal, mSources, mKeyboardType, mKeyCharacterMap,
- mCountryCode, mHasVibrator, mHasMicrophone, mHasButtonUnderPad, mHasSensor,
- mHasBattery, mSupportsUsi);
+ mCountryCode, mKeyboardLanguageTag, mKeyboardLayoutType, mHasVibrator,
+ mHasMicrophone, mHasButtonUnderPad, mHasSensor, mHasBattery, mSupportsUsi);
}
}
@@ -888,7 +919,30 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Returns the keyboard language as an IETF
+ * <a href="https://tools.ietf.org/html/bcp47">BCP-47</a>
+ * conformant tag if available.
+ *
+ * @hide
+ */
+ @Nullable
+ public String getKeyboardLanguageTag() {
+ return mKeyboardLanguageTag;
+ }
+
+ /**
+ * Returns the keyboard layout type if available.
+ *
+ * @hide
+ */
+ @Nullable
+ public String getKeyboardLayoutType() {
+ return mKeyboardLayoutType;
+ }
+
+ /**
* Gets whether the device is capable of producing the list of keycodes.
+ *
* @param keys The list of android keycodes to check for.
* @return An array of booleans where each member specifies whether the device is capable of
* generating the keycode given by the corresponding value at the same index in the keys array.
@@ -1340,6 +1394,8 @@ public final class InputDevice implements Parcelable {
out.writeInt(mSources);
out.writeInt(mKeyboardType);
out.writeInt(mCountryCode);
+ out.writeString8(mKeyboardLanguageTag);
+ out.writeString8(mKeyboardLayoutType);
out.writeInt(mHasVibrator ? 1 : 0);
out.writeInt(mHasMicrophone ? 1 : 0);
out.writeInt(mHasButtonUnderPad ? 1 : 0);
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 5c4305cc1647..2228b9fe3827 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,11 +16,8 @@
package android.view;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.window.WindowProviderService.isWindowProviderService;
import android.annotation.CallbackExecutor;
@@ -28,12 +25,9 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
-import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
@@ -42,6 +36,7 @@ import android.os.StrictMode;
import android.window.ITaskFpsCallback;
import android.window.TaskFpsCallback;
import android.window.WindowContext;
+import android.window.WindowMetricsController;
import android.window.WindowProvider;
import com.android.internal.annotations.GuardedBy;
@@ -49,7 +44,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -107,6 +101,10 @@ public final class WindowManagerImpl implements WindowManager {
private final ArrayList<OnFpsCallbackListenerProxy> mOnFpsCallbackListenerProxies =
new ArrayList<>();
+ /** A controller to handle {@link WindowMetrics} related APIs */
+ @NonNull
+ private final WindowMetricsController mWindowMetricsController;
+
public WindowManagerImpl(Context context) {
this(context, null /* parentWindow */, null /* clientToken */);
}
@@ -116,6 +114,7 @@ public final class WindowManagerImpl implements WindowManager {
mContext = context;
mParentWindow = parentWindow;
mWindowContextToken = windowContextToken;
+ mWindowMetricsController = new WindowMetricsController(mContext);
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
@@ -293,112 +292,18 @@ public final class WindowManagerImpl implements WindowManager {
@Override
public WindowMetrics getCurrentWindowMetrics() {
- final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
- final Rect bounds = getCurrentBounds(context);
-
- return new WindowMetrics(bounds, computeWindowInsets(bounds));
- }
-
- private static Rect getCurrentBounds(Context context) {
- synchronized (ResourcesManager.getInstance()) {
- return context.getResources().getConfiguration().windowConfiguration.getBounds();
- }
+ return mWindowMetricsController.getCurrentWindowMetrics();
}
@Override
public WindowMetrics getMaximumWindowMetrics() {
- final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
- final Rect maxBounds = getMaximumBounds(context);
-
- return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds));
- }
-
- private static Rect getMaximumBounds(Context context) {
- synchronized (ResourcesManager.getInstance()) {
- return context.getResources().getConfiguration().windowConfiguration.getMaxBounds();
- }
- }
-
- private WindowInsets computeWindowInsets(Rect bounds) {
- // Initialize params which used for obtaining all system insets.
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
- final Context context = (mParentWindow != null) ? mParentWindow.getContext() : mContext;
- params.token = Context.getToken(context);
- return getWindowInsetsFromServerForCurrentDisplay(params, bounds);
- }
-
- private WindowInsets getWindowInsetsFromServerForCurrentDisplay(
- WindowManager.LayoutParams attrs, Rect bounds) {
- final Configuration config = mContext.getResources().getConfiguration();
- return getWindowInsetsFromServerForDisplay(mContext.getDisplayId(), attrs, bounds,
- config.isScreenRound(), config.windowConfiguration.getWindowingMode());
- }
-
- /**
- * Retrieves WindowInsets for the given context and display, given the window bounds.
- *
- * @param displayId the ID of the logical display to calculate insets for
- * @param attrs the LayoutParams for the calling app
- * @param bounds the window bounds to calculate insets for
- * @param isScreenRound if the display identified by displayId is round
- * @param windowingMode the windowing mode of the window to calculate insets for
- * @return WindowInsets calculated for the given window bounds, on the given display
- */
- private static WindowInsets getWindowInsetsFromServerForDisplay(int displayId,
- WindowManager.LayoutParams attrs, Rect bounds, boolean isScreenRound,
- int windowingMode) {
- try {
- final InsetsState insetsState = new InsetsState();
- final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
- .getWindowInsets(attrs, displayId, insetsState);
- return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
- isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
- SYSTEM_UI_FLAG_VISIBLE, attrs.type, windowingMode,
- null /* typeSideMap */);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mWindowMetricsController.getMaximumWindowMetrics();
}
@Override
@NonNull
public Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
- List<DisplayInfo> possibleDisplayInfos;
- try {
- possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService()
- .getPossibleDisplayInfo(displayId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- Set<WindowMetrics> maxMetrics = new HashSet<>();
- WindowInsets windowInsets;
- DisplayInfo currentDisplayInfo;
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
- for (int i = 0; i < possibleDisplayInfos.size(); i++) {
- currentDisplayInfo = possibleDisplayInfos.get(i);
-
- // Calculate max bounds for this rotation and state.
- Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
- currentDisplayInfo.logicalHeight);
-
- // Calculate insets for the rotated max bounds.
- final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
- // Initialize insets based upon display rotation. Note any window-provided insets
- // will not be set.
- windowInsets = getWindowInsetsFromServerForDisplay(
- currentDisplayInfo.displayId, params,
- new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
- currentDisplayInfo.getNaturalHeight()), isScreenRound,
- WINDOWING_MODE_FULLSCREEN);
- // Set the hardware-provided insets.
- windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners(
- currentDisplayInfo.roundedCorners)
- .setDisplayCutout(currentDisplayInfo.displayCutout).build();
-
- maxMetrics.add(new WindowMetrics(maxBounds, windowInsets));
- }
- return maxMetrics;
+ return mWindowMetricsController.getPossibleMaximumWindowMetrics(displayId);
}
@Override
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index a52a99bec500..33b763bbf0c0 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -304,6 +304,20 @@ import java.util.List;
* </ul>
* </p>
* <p>
+ * <b>View scrolled to</b> - represents the event of a target node brought on screen by
+ * ACTION_SCROLL_IN_DIRECTION.
+ * <em>Type:</em> {@link #TYPE_VIEW_TARGETED_BY_SCROLL}</br>
+ * <em>Properties:</em></br>
+ * <ul>
+ * <li>{@link #getEventType()} - The type of the event.</li>
+ * <li>{@link #getSource()} - The source info (for registered clients). This represents the node
+ * that is brought on screen as a result of the scroll.</li>
+ * <li>{@link #getClassName()} - The class name of the source.</li>
+ * <li>{@link #getPackageName()} - The package name of the source.</li>
+ * <li>{@link #getEventTime()} - The event time.</li>
+ * </ul>
+ * </p>
+ * <p>
* <b>Touch interaction start</b> - represents the event of starting a touch
* interaction, which is the user starts touching the screen.</br>
* <em>Type:</em> {@link #TYPE_TOUCH_INTERACTION_START}</br>
@@ -596,6 +610,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
public static final int TYPE_SPEECH_STATE_CHANGE = 0x02000000;
/**
+ * Represents the event of a scroll having completed and brought the target node on screen.
+ */
+ public static final int TYPE_VIEW_TARGETED_BY_SCROLL = 0x04000000;
+
+ /**
* Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: The type of change is not
* defined.
*/
@@ -899,7 +918,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
TYPE_WINDOWS_CHANGED,
TYPE_VIEW_CONTEXT_CLICKED,
TYPE_ASSIST_READING_CONTEXT,
- TYPE_SPEECH_STATE_CHANGE
+ TYPE_SPEECH_STATE_CHANGE,
+ TYPE_VIEW_TARGETED_BY_SCROLL
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {}
@@ -929,6 +949,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @see #TYPE_TOUCH_INTERACTION_END
* @see #TYPE_WINDOWS_CHANGED
* @see #TYPE_VIEW_CONTEXT_CLICKED
+ * @see #TYPE_VIEW_TARGETED_BY_SCROLL
*/
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
@@ -1729,6 +1750,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
case TYPE_VIEW_CONTEXT_CLICKED: return "TYPE_VIEW_CONTEXT_CLICKED";
case TYPE_ASSIST_READING_CONTEXT: return "TYPE_ASSIST_READING_CONTEXT";
case TYPE_SPEECH_STATE_CHANGE: return "TYPE_SPEECH_STATE_CHANGE";
+ case TYPE_VIEW_TARGETED_BY_SCROLL: return "TYPE_VIEW_TARGETED_BY_SCROLL";
default: return Integer.toHexString(eventType);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index b91199dc0ae5..5629e0f15043 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -659,6 +659,25 @@ public class AccessibilityNodeInfo implements Parcelable {
public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT =
"android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
+ /**
+ * <p>Argument to represent the direction when using
+ * {@link AccessibilityAction#ACTION_SCROLL_IN_DIRECTION}.</p>
+ *
+ * <p>
+ * The value of this argument can be one of:
+ * <ul>
+ * <li>{@link View#FOCUS_DOWN}</li>
+ * <li>{@link View#FOCUS_UP}</li>
+ * <li>{@link View#FOCUS_LEFT}</li>
+ * <li>{@link View#FOCUS_RIGHT}</li>
+ * <li>{@link View#FOCUS_FORWARD}</li>
+ * <li>{@link View#FOCUS_BACKWARD}</li>
+ * </ul>
+ * </p>
+ */
+ public static final String ACTION_ARGUMENT_DIRECTION_INT =
+ "android.view.accessibility.action.ARGUMENT_DIRECTION_INT";
+
// Focus types
/**
@@ -4682,6 +4701,9 @@ public class AccessibilityNodeInfo implements Parcelable {
if (action == R.id.accessibilityActionShowTextSuggestions) {
return "ACTION_SHOW_TEXT_SUGGESTIONS";
}
+ if (action == R.id.accessibilityActionScrollInDirection) {
+ return "ACTION_SCROLL_IN_DIRECTION";
+ }
return "ACTION_UNKNOWN";
}
}
@@ -5218,6 +5240,32 @@ public class AccessibilityNodeInfo implements Parcelable {
new AccessibilityAction(R.id.accessibilityActionScrollToPosition);
/**
+ * Action that brings fully on screen the next node in the specified direction.
+ *
+ * <p>
+ * This should include wrapping around to the next/previous row, column, etc. in a
+ * collection if one is available. If there is no node in that direction, the action
+ * should fail and return false.
+ * </p>
+ * <p>
+ * This action should be used instead of
+ * {@link AccessibilityAction#ACTION_SCROLL_TO_POSITION} when a widget does not have
+ * clear row and column semantics or if a directional search is needed to find a node in
+ * a complex ViewGroup where individual nodes may span multiple rows or columns. The
+ * implementing widget must send a
+ * {@link AccessibilityEvent#TYPE_VIEW_TARGETED_BY_SCROLL} accessibility event with the
+ * scroll target as the source. An accessibility service can listen for this event,
+ * inspect its source, and use the result when determining where to place accessibility
+ * focus.
+ * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_DIRECTION_INT}. This is a
+ * required argument.<br>
+ * </p>
+ */
+ @NonNull public static final AccessibilityAction ACTION_SCROLL_IN_DIRECTION =
+ new AccessibilityAction(R.id.accessibilityActionScrollInDirection);
+
+ /**
* Action to scroll the node content up.
*/
public static final AccessibilityAction ACTION_SCROLL_UP =
diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS
index 108c42cdde2a..26c59a68fd53 100644
--- a/core/java/android/view/autofill/OWNERS
+++ b/core/java/android/view/autofill/OWNERS
@@ -1,7 +1,10 @@
# Bug component: 351486
augale@google.com
+haoranzhang@google.com
joannechung@google.com
markpun@google.com
lpeter@google.com
+simranjit@google.com
tymtsai@google.com
+yunicorn@google.com
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index e027934ccb51..535dc4e78c89 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.annotation.NonNull;
import android.app.WindowConfiguration;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.util.ArraySet;
@@ -114,7 +115,7 @@ public abstract class DisplayWindowPolicyController {
/**
* Returns {@code true} if the given new task can be launched on this virtual display.
*/
- public abstract boolean canActivityBeLaunched(@NonNull ActivityInfo activityInfo,
+ public abstract boolean canActivityBeLaunched(@NonNull ActivityInfo activityInfo, Intent intent,
@WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
boolean isNewTask);
diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java
new file mode 100644
index 000000000000..47d532c31dad
--- /dev/null
+++ b/core/java/android/window/WindowMetricsController.java
@@ -0,0 +1,172 @@
+/*
+ * 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 static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+
+import android.annotation.NonNull;
+import android.app.ResourcesManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.InsetsState;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowMetrics;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A controller to handle {@link android.view.WindowMetrics} related APIs, which are
+ * <ol>
+ * <li>{@link WindowManager#getCurrentWindowMetrics()}</li>
+ * <li>{@link WindowManager#getMaximumWindowMetrics()}</li>
+ * <li>{@link WindowManager#getPossibleMaximumWindowMetrics(int)}</li>
+ * </ol>
+ *
+ * @hide
+ */
+public final class WindowMetricsController {
+ private final Context mContext;
+
+ public WindowMetricsController(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /** @see WindowManager#getCurrentWindowMetrics() */
+ public WindowMetrics getCurrentWindowMetrics() {
+ final Rect bounds = getCurrentBounds(mContext);
+
+ // TODO(b/187712731): Provide density for WindowMetrics.
+ return new WindowMetrics(bounds, computeWindowInsets(bounds));
+ }
+
+ private static Rect getCurrentBounds(Context context) {
+ synchronized (ResourcesManager.getInstance()) {
+ return context.getResources().getConfiguration().windowConfiguration.getBounds();
+ }
+ }
+
+ /** @see WindowManager#getMaximumWindowMetrics() */
+ public WindowMetrics getMaximumWindowMetrics() {
+ final Rect maxBounds = getMaximumBounds(mContext);
+
+ // TODO(b/187712731): Provide density for WindowMetrics.
+ return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds));
+ }
+
+ private static Rect getMaximumBounds(Context context) {
+ synchronized (ResourcesManager.getInstance()) {
+ return context.getResources().getConfiguration().windowConfiguration.getMaxBounds();
+ }
+ }
+
+ private WindowInsets computeWindowInsets(Rect bounds) {
+ // Initialize params which used for obtaining all system insets.
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.token = Context.getToken(mContext);
+ return getWindowInsetsFromServerForCurrentDisplay(params, bounds);
+ }
+
+ private WindowInsets getWindowInsetsFromServerForCurrentDisplay(
+ WindowManager.LayoutParams attrs, Rect bounds) {
+ final boolean isScreenRound;
+ final int windowingMode;
+ synchronized (ResourcesManager.getInstance()) {
+ final Configuration config = mContext.getResources().getConfiguration();
+ isScreenRound = config.isScreenRound();
+ windowingMode = config.windowConfiguration.getWindowingMode();
+ }
+ return getWindowInsetsFromServerForDisplay(mContext.getDisplayId(), attrs, bounds,
+ isScreenRound, windowingMode);
+ }
+
+ /**
+ * Retrieves WindowInsets for the given context and display, given the window bounds.
+ *
+ * @param displayId the ID of the logical display to calculate insets for
+ * @param attrs the LayoutParams for the calling app
+ * @param bounds the window bounds to calculate insets for
+ * @param isScreenRound if the display identified by displayId is round
+ * @param windowingMode the windowing mode of the window to calculate insets for
+ * @return WindowInsets calculated for the given window bounds, on the given display
+ */
+ private static WindowInsets getWindowInsetsFromServerForDisplay(int displayId,
+ WindowManager.LayoutParams attrs, Rect bounds, boolean isScreenRound,
+ int windowingMode) {
+ try {
+ final InsetsState insetsState = new InsetsState();
+ final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
+ .getWindowInsets(attrs, displayId, insetsState);
+ return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
+ isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
+ SYSTEM_UI_FLAG_VISIBLE, attrs.type, windowingMode,
+ null /* typeSideMap */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @see WindowManager#getPossibleMaximumWindowMetrics(int) */
+ @NonNull
+ public Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
+ List<DisplayInfo> possibleDisplayInfos;
+ try {
+ possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService()
+ .getPossibleDisplayInfo(displayId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ Set<WindowMetrics> maxMetrics = new HashSet<>();
+ WindowInsets windowInsets;
+ DisplayInfo currentDisplayInfo;
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ for (int i = 0; i < possibleDisplayInfos.size(); i++) {
+ currentDisplayInfo = possibleDisplayInfos.get(i);
+
+ // Calculate max bounds for this rotation and state.
+ Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
+ currentDisplayInfo.logicalHeight);
+
+ // Calculate insets for the rotated max bounds.
+ final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
+ // Initialize insets based upon display rotation. Note any window-provided insets
+ // will not be set.
+ windowInsets = getWindowInsetsFromServerForDisplay(
+ currentDisplayInfo.displayId, params,
+ new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
+ currentDisplayInfo.getNaturalHeight()), isScreenRound,
+ WINDOWING_MODE_FULLSCREEN);
+ // Set the hardware-provided insets.
+ windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners(
+ currentDisplayInfo.roundedCorners)
+ .setDisplayCutout(currentDisplayInfo.displayCutout).build();
+
+ maxMetrics.add(new WindowMetrics(maxBounds, windowInsets));
+ }
+ return maxMetrics;
+ }
+}
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 4b7b91c74f94..afb526aeea6f 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -37,12 +37,17 @@ import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPOR
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 static com.android.internal.util.LatencyTracker.ActionProperties.ENABLE_SUFFIX;
+import static com.android.internal.util.LatencyTracker.ActionProperties.LEGACY_TRACE_THRESHOLD_SUFFIX;
+import static com.android.internal.util.LatencyTracker.ActionProperties.SAMPLE_INTERVAL_SUFFIX;
+import static com.android.internal.util.LatencyTracker.ActionProperties.TRACE_THRESHOLD_SUFFIX;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Build;
+import android.os.ConditionVariable;
import android.os.SystemClock;
import android.os.Trace;
import android.provider.DeviceConfig;
@@ -58,6 +63,7 @@ import com.android.internal.os.BackgroundThread;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
@@ -261,11 +267,11 @@ public class LatencyTracker {
@GuardedBy("mLock")
private final SparseArray<Session> mSessions = new SparseArray<>();
@GuardedBy("mLock")
- private final int[] mTraceThresholdPerAction = new int[ACTIONS_ALL.length];
- @GuardedBy("mLock")
- private int mSamplingInterval;
+ private final SparseArray<ActionProperties> mActionPropertiesMap = new SparseArray<>();
@GuardedBy("mLock")
private boolean mEnabled;
+ @VisibleForTesting
+ public final ConditionVariable mDeviceConfigPropertiesUpdated = new ConditionVariable();
public static LatencyTracker getInstance(Context context) {
if (sLatencyTracker == null) {
@@ -278,9 +284,9 @@ public class LatencyTracker {
return sLatencyTracker;
}
- private LatencyTracker() {
+ @VisibleForTesting
+ public LatencyTracker() {
mEnabled = DEFAULT_ENABLED;
- mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
// Post initialization to the background in case we're running on the main thread.
BackgroundThread.getHandler().post(() -> this.updateProperties(
@@ -291,14 +297,24 @@ public class LatencyTracker {
private void updateProperties(DeviceConfig.Properties properties) {
synchronized (mLock) {
- mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+ int samplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
DEFAULT_SAMPLING_INTERVAL);
mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
for (int action : ACTIONS_ALL) {
- mTraceThresholdPerAction[action] =
- properties.getInt(getNameOfAction(STATSD_ACTION[action]), -1);
+ String actionName = getNameOfAction(STATSD_ACTION[action]).toLowerCase(Locale.ROOT);
+ int legacyActionTraceThreshold = properties.getInt(
+ actionName + LEGACY_TRACE_THRESHOLD_SUFFIX, -1);
+ mActionPropertiesMap.put(action, new ActionProperties(action,
+ properties.getBoolean(actionName + ENABLE_SUFFIX, mEnabled),
+ properties.getInt(actionName + SAMPLE_INTERVAL_SUFFIX, samplingInterval),
+ properties.getInt(actionName + TRACE_THRESHOLD_SUFFIX,
+ legacyActionTraceThreshold)));
+ }
+ if (DEBUG) {
+ Log.d(TAG, "updated action properties: " + mActionPropertiesMap);
}
}
+ mDeviceConfigPropertiesUpdated.open();
}
/**
@@ -369,16 +385,38 @@ public class LatencyTracker {
return "com.android.telemetry.latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
}
+ /**
+ * @deprecated Use {@link #isEnabled(Context, int)}
+ */
+ @Deprecated
public static boolean isEnabled(Context ctx) {
return getInstance(ctx).isEnabled();
}
+ /**
+ * @deprecated Used {@link #isEnabled(int)}
+ */
+ @Deprecated
public boolean isEnabled() {
synchronized (mLock) {
return mEnabled;
}
}
+ public static boolean isEnabled(Context ctx, int action) {
+ return getInstance(ctx).isEnabled(action);
+ }
+
+ public boolean isEnabled(int action) {
+ synchronized (mLock) {
+ ActionProperties actionProperties = mActionPropertiesMap.get(action);
+ if (actionProperties != null) {
+ return actionProperties.isEnabled();
+ }
+ return false;
+ }
+ }
+
/**
* Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
*
@@ -468,8 +506,14 @@ public class LatencyTracker {
boolean shouldSample;
int traceThreshold;
synchronized (mLock) {
- shouldSample = ThreadLocalRandom.current().nextInt(mSamplingInterval) == 0;
- traceThreshold = mTraceThresholdPerAction[action];
+ ActionProperties actionProperties = mActionPropertiesMap.get(action);
+ if (actionProperties == null) {
+ return;
+ }
+ int nextRandNum = ThreadLocalRandom.current().nextInt(
+ actionProperties.getSamplingInterval());
+ shouldSample = nextRandNum == 0;
+ traceThreshold = actionProperties.getTraceThreshold();
}
if (traceThreshold > 0 && duration >= traceThreshold) {
@@ -549,4 +593,59 @@ public class LatencyTracker {
return (int) (mEndRtc - mStartRtc);
}
}
+
+ @VisibleForTesting
+ static class ActionProperties {
+ static final String ENABLE_SUFFIX = "_enable";
+ static final String SAMPLE_INTERVAL_SUFFIX = "_sample_interval";
+ // TODO: migrate all usages of the legacy trace theshold property
+ static final String LEGACY_TRACE_THRESHOLD_SUFFIX = "";
+ static final String TRACE_THRESHOLD_SUFFIX = "_trace_threshold";
+
+ @Action
+ private final int mAction;
+ private final boolean mEnabled;
+ private final int mSamplingInterval;
+ private final int mTraceThreshold;
+
+ ActionProperties(
+ @Action int action,
+ boolean enabled,
+ int samplingInterval,
+ int traceThreshold) {
+ this.mAction = action;
+ com.android.internal.util.AnnotationValidations.validate(
+ Action.class, null, mAction);
+ this.mEnabled = enabled;
+ this.mSamplingInterval = samplingInterval;
+ this.mTraceThreshold = traceThreshold;
+ }
+
+ @Action
+ int getAction() {
+ return mAction;
+ }
+
+ boolean isEnabled() {
+ return mEnabled;
+ }
+
+ int getSamplingInterval() {
+ return mSamplingInterval;
+ }
+
+ int getTraceThreshold() {
+ return mTraceThreshold;
+ }
+
+ @Override
+ public String toString() {
+ return "ActionProperties{"
+ + " mAction=" + mAction
+ + ", mEnabled=" + mEnabled
+ + ", mSamplingInterval=" + mSamplingInterval
+ + ", mTraceThreshold=" + mTraceThreshold
+ + "}";
+ }
+ }
}
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index b2994f41af4b..02f6a77828c6 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -48,6 +48,18 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
return NULL;
}
+ std::optional<KeyboardLayoutInfo> layoutInfo = deviceInfo.getKeyboardLayoutInfo();
+ ScopedLocalRef<jstring> keyboardLanguageTagObj(env,
+ env->NewStringUTF(
+ layoutInfo
+ ? layoutInfo->languageTag.c_str()
+ : NULL));
+ ScopedLocalRef<jstring> keyboardLayoutTypeObj(env,
+ env->NewStringUTF(
+ layoutInfo
+ ? layoutInfo->layoutType.c_str()
+ : NULL));
+
ScopedLocalRef<jobject> kcmObj(env,
android_view_KeyCharacterMap_create(env, deviceInfo.getId(),
deviceInfo.getKeyCharacterMap()));
@@ -66,7 +78,8 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
static_cast<int32_t>(ident.product), descriptorObj.get(),
deviceInfo.isExternal(), deviceInfo.getSources(),
deviceInfo.getKeyboardType(), kcmObj.get(),
- deviceInfo.getCountryCode(), deviceInfo.hasVibrator(),
+ deviceInfo.getCountryCode(), keyboardLanguageTagObj.get(),
+ keyboardLayoutTypeObj.get(), deviceInfo.hasVibrator(),
deviceInfo.hasMic(), deviceInfo.hasButtonUnderPad(),
deviceInfo.hasSensor(), deviceInfo.hasBattery(),
deviceInfo.supportsUsi()));
@@ -91,10 +104,10 @@ int register_android_view_InputDevice(JNIEnv* env)
gInputDeviceClassInfo.clazz = FindClassOrDie(env, "android/view/InputDevice");
gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz);
- gInputDeviceClassInfo.ctor =
- GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
- "(IIILjava/lang/String;IILjava/lang/"
- "String;ZIILandroid/view/KeyCharacterMap;IZZZZZZ)V");
+ gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
+ "(IIILjava/lang/String;IILjava/lang/"
+ "String;ZIILandroid/view/KeyCharacterMap;ILjava/"
+ "lang/String;Ljava/lang/String;ZZZZZZ)V");
gInputDeviceClassInfo.addMotionRange = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz,
"addMotionRange", "(IIFFFFF)V");
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index aa8edf954187..bc6df985e350 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -816,6 +816,7 @@
<protected-broadcast android:name="android.intent.action.PROFILE_REMOVED" />
<protected-broadcast android:name="com.android.internal.telephony.cat.SMS_SENT_ACTION" />
<protected-broadcast android:name="com.android.internal.telephony.cat.SMS_DELIVERY_ACTION" />
+ <protected-broadcast android:name="android.companion.virtual.action.VIRTUAL_DEVICE_REMOVED" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
@@ -6849,6 +6850,11 @@
<permission android:name="android.permission.RUN_LONG_JOBS"
android:protectionLevel="normal|appop"/>
+ <!-- Allows an app access to the installer provided app metadata.
+ @hide -->
+ <permission android:name="android.permission.GET_APP_METADATA"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index f0cee4d99e68..d3692b8ffa71 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Laat ’n metgeselapp toe om metgeselboodskappe aan ander toestelle af te lewer."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Begin voorgronddienste van agtergrond af"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Laat ’n metgeselapp toe om voorgronddienste van agtergrond af te begin"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index d963e4f26c5a..341e6237ca16 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -392,14 +392,10 @@
<string name="permdesc_runInBackground" msgid="4344539472115495141">"ይህ መተግበሪያ በጀርባ ላይ ማሄድ ይችላል። ይሄ ባትሪውን በይበልጥ ሊጨርሰው ይችላል።"</string>
<string name="permlab_useDataInBackground" msgid="783415807623038947">"በጀርባ ላይ ውሂብ ይጠቀማል"</string>
<string name="permdesc_useDataInBackground" msgid="1230753883865891987">"ይህ መተግበሪያ በጀርባ ላይ ውሂብ ሊጠቀም ይችላል። ይሄ የውሂብ ፍጆታን ሊጨምር ይችላል።"</string>
- <!-- no translation found for permlab_schedule_exact_alarm (6683283918033029730) -->
- <skip />
- <!-- no translation found for permdesc_schedule_exact_alarm (8198009212013211497) -->
- <skip />
- <!-- no translation found for permlab_use_exact_alarm (348045139777131552) -->
- <skip />
- <!-- no translation found for permdesc_use_exact_alarm (7033761461886938912) -->
- <skip />
+ <string name="permlab_schedule_exact_alarm" msgid="6683283918033029730">"በትክክል በጊዜ የተያዙ እርምጃዎችን መርሐግብር ያስይዙ"</string>
+ <string name="permdesc_schedule_exact_alarm" msgid="8198009212013211497">"ይህ መተግበሪያ ሥራ ወደፊት በሚፈለገው ጊዜ እንዲከናወን መርሐግብር ማስያዝ ይችላል። እንዲሁም ይህ ማለት እርስዎ መሣሪያውን በንቃት በማይጠቀሙበት ጊዜ መተግበሪያው ሊያሄድ ይችላል ማለት ነው።"</string>
+ <string name="permlab_use_exact_alarm" msgid="348045139777131552">"ማንቂያዎችን ወይም የክስተት አስታዋሾችን መርሐግብር ያስይዙ"</string>
+ <string name="permdesc_use_exact_alarm" msgid="7033761461886938912">"ይህ መተግበሪያ ወደፊት በሚፈለግበት ጊዜ እርስዎን ለማሳወቅ እንደ ማንቂያዎች እና አስታዋሾች ያሉ እርምጃዎችን መርሐግብር ማስያዝ ይችላል።"</string>
<string name="permlab_persistentActivity" msgid="464970041740567970">"ትግበራ ሁልጊዜ አሂድ ላይ አድርግ"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"መተግበሪያው የራሱን ክፍሎች በማህደረ ትውስታ ውስጥ በቋሚነት የሚቀጥሉ እንዲያደርግ ይፈቅድለታል። ይህ ለሌላ መተግበሪያዎች ያለውን ማህደረ ትውስታ በመገደብ ጡባዊ ተኮውን ሊያንቀራፍፈው ይችላል።"</string>
<string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"መተግበሪያው የራሱን ክፍሎች በማህደረ ትውስታ ውስጥ በቋሚነት የሚቀጥሉ እንዲያደርግ ይፈቅድለታል። ይህ ለሌላ መተግበሪያዎች ያለውን ማህደረ ትውስታ በመገደብ የእርስዎን Android TV ሊያንቀራፍፈው ይችላል።"</string>
@@ -428,10 +424,8 @@
<string name="permdesc_foregroundServiceRemoteMessaging" msgid="8767598075877576277">"መተግበሪያው የፊት አገልግሎትን በ«remoteMessaging» ዓይነት እንዲጠቀም ይፈቅዳል"</string>
<string name="permlab_foregroundServiceSystemExempted" msgid="1597663713590612685">"የፊት አገልግሎትን በ«systemExempted» ዓይነት ማስሄድ"</string>
<string name="permdesc_foregroundServiceSystemExempted" msgid="947381760834649622">"መተግበሪያው የፊት አገልግሎትን በ«systemExempted» ዓይነት እንዲጠቀም ይፈቅዳል"</string>
- <!-- no translation found for permlab_foregroundServiceFileManagement (2585000987966045030) -->
- <skip />
- <!-- no translation found for permdesc_foregroundServiceFileManagement (417103601269698508) -->
- <skip />
+ <string name="permlab_foregroundServiceFileManagement" msgid="2585000987966045030">"የፊት አገልግሎትን በ«fileManagement» ዓይነት ማስሄድ"</string>
+ <string name="permdesc_foregroundServiceFileManagement" msgid="417103601269698508">"መተግበሪያው የፊት አገልግሎቶችን በ«fileManagement» ዓይነት እንዲጠቀም ያስችላል"</string>
<string name="permlab_foregroundServiceSpecialUse" msgid="7973536745876645082">"የፊት አገልግሎትን በ«specialUse» ዓይነት ማስሄድ"</string>
<string name="permdesc_foregroundServiceSpecialUse" msgid="646713654541885919">"መተግበሪያው የፊት አገልግሎትን በ«specialUse» ዓይነት እንዲጠቀም ይፈቅዳል"</string>
<string name="permlab_getPackageSize" msgid="375391550792886641">"የመተግበሪያ ማከማቻ ቦታ ለካ"</string>
@@ -2350,20 +2344,16 @@
<string name="vdm_pip_blocked" msgid="4036107522497281397">"በዥረት በመልቀቅ ወቅት በሥዕል-ላይ-ሥዕል ማየት አይችሉም"</string>
<string name="system_locale_title" msgid="711882686834677268">"የሥርዓት ነባሪ"</string>
<string name="default_card_name" msgid="9198284935962911468">"ካርድ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
- <!-- no translation found for permlab_companionProfileWatch (2457738382085872542) -->
- <skip />
- <!-- no translation found for permdesc_companionProfileWatch (5655698581110449397) -->
- <skip />
- <!-- no translation found for permlab_observeCompanionDevicePresence (9008994909653990465) -->
- <skip />
- <!-- no translation found for permdesc_observeCompanionDevicePresence (3011699826788697852) -->
- <skip />
- <!-- no translation found for permlab_deliverCompanionMessages (3931552294842980887) -->
- <skip />
- <!-- no translation found for permdesc_deliverCompanionMessages (2170847384281412850) -->
- <skip />
- <!-- no translation found for permlab_startForegroundServicesFromBackground (6363004936218638382) -->
+ <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"የእጅ ሰዓቶችን ለማስተዳደር የአጃቢ የእጅ ሰዓት መገለጫ ፍቃድ"</string>
+ <string name="permdesc_companionProfileWatch" msgid="5655698581110449397">"አጃቢ መተግበሪያ የእጅ ሰዓቶችን እንዲያስተዳድር ያስችላል።"</string>
+ <string name="permlab_observeCompanionDevicePresence" msgid="9008994909653990465">"የአጃቢ መሣሪያ ተገኝነትን ተመልከት"</string>
+ <string name="permdesc_observeCompanionDevicePresence" msgid="3011699826788697852">"አጃቢ መተግበሪያ መሳሪያዎቹ በአቅራቢያ ሲሆኑ ወይም ሩቅ ሲሆኑ የአጃቢ መሳሪያ መኖሩን ለማየት ያስችላል።"</string>
+ <string name="permlab_deliverCompanionMessages" msgid="3931552294842980887">"አጃቢ መልዕክቶችን አድርስ"</string>
+ <string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"አጃቢ መተግበሪያ አጃቢ መልዕክቶችን ወደ ሌሎች መሣሪያዎች እንዲያደርስ ያስችላል።"</string>
+ <string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"የፊት አገልግሎቶችን ከዳራ ይጀምሩ"</string>
+ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"አጃቢ መተግበሪያ ከዳራ የፊት አገልግሎቶችን እንዲጀምር ያስችላል።"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
<skip />
- <!-- no translation found for permdesc_startForegroundServicesFromBackground (4071826571656001537) -->
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
<skip />
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 52381f9bf240..ad1d34f64bc8 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -2356,4 +2356,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"يسمح هذا الإذن للتطبيق المصاحب بتسليم الرسائل المصاحبة إلى الأجهزة الأخرى."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"بدء الخدمات التي تعمل في المقدّمة من الخلفية"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"يسمح هذا الإذن للتطبيق المصاحب ببدء الخدمات التي تعمل في المقدّمة من الخلفية."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 2b6debc09f39..31b62be842ef 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"এটা সহযোগী এপক অন্য ডিভাইচলৈ সহযোগী বাৰ্তাসমূহ ডেলিভাৰ কৰিবলৈ দিয়ে।"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"নেপথ্যৰ পৰা অগ্ৰভূমি সেৱাসমূহ আৰম্ভ কৰক"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"এটা সহযোগী এপক নেপথ্যৰ পৰা অগ্ৰভূমি সেৱাসমূহ আৰম্ভ কৰিবলৈ দিয়ে।"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index b93eb9ac7467..253a894e078b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Kompanyon tətbiqinə kompanyon mesajlarını digər cihazlara çatdırmaq icazəsi verir."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Ön fon xidmətlərini arxa fondan başlatmaq"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Kompanyon tətbiqinə ön fon xidmətlərini arxa fondan başlatmaq icazəsi verir."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-b+sr+Latn-television/strings.xml b/core/res/res/values-b+sr+Latn-television/strings.xml
index df5d8eae0779..86435930440c 100644
--- a/core/res/res/values-b+sr+Latn-television/strings.xml
+++ b/core/res/res/values-b+sr+Latn-television/strings.xml
@@ -17,6 +17,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon je blokiran"</string>
- <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera je blokirana"</string>
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Микрофон је блокиран"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камера је блокирана"</string>
</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 217908e4db90..562ffec6a1a1 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -22,844 +22,844 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="202579285008794431">"B"</string>
<string name="fileSizeSuffix" msgid="4233671691980131257">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
- <string name="untitled" msgid="3381766946944136678">"&lt;Bez imena&gt;"</string>
- <string name="emptyPhoneNumber" msgid="5812172618020360048">"(Nema broja telefona)"</string>
- <string name="unknownName" msgid="7078697621109055330">"Nepoznato"</string>
- <string name="defaultVoiceMailAlphaTag" msgid="2190754495304236490">"Glasovna pošta"</string>
+ <string name="untitled" msgid="3381766946944136678">"&lt;Без имена&gt;"</string>
+ <string name="emptyPhoneNumber" msgid="5812172618020360048">"(Нема броја телефона)"</string>
+ <string name="unknownName" msgid="7078697621109055330">"Непознато"</string>
+ <string name="defaultVoiceMailAlphaTag" msgid="2190754495304236490">"Гласовна пошта"</string>
<string name="defaultMsisdnAlphaTag" msgid="2285034592902077488">"MSISDN1"</string>
- <string name="mmiError" msgid="2862759606579822246">"Problemi sa vezom ili nevažeći MMI kôd."</string>
- <string name="mmiErrorNotSupported" msgid="5001803469335286099">"Funkcija nije podržana."</string>
- <string name="mmiFdnError" msgid="3975490266767565852">"Rad je ograničen samo na brojeve fiksnog biranja."</string>
- <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"Ne možete da promenite podešavanja preusmeravanja poziva sa telefona dok ste u romingu."</string>
- <string name="serviceEnabled" msgid="7549025003394765639">"Usluga je omogućena."</string>
- <string name="serviceEnabledFor" msgid="1463104778656711613">"Usluga je omogućena za:"</string>
- <string name="serviceDisabled" msgid="641878791205871379">"Usluga je onemogućena."</string>
- <string name="serviceRegistered" msgid="3856192211729577482">"Registracija je uspela."</string>
- <string name="serviceErased" msgid="997354043770513494">"Brisanje je dovršeno."</string>
- <string name="passwordIncorrect" msgid="917087532676155877">"Neispravna lozinka."</string>
- <string name="mmiComplete" msgid="6341884570892520140">"MMI kôd je izvršen."</string>
- <string name="badPin" msgid="888372071306274355">"Stari PIN koji ste uneli nije tačan."</string>
- <string name="badPuk" msgid="4232069163733147376">"PUK koji ste uneli nije tačan."</string>
- <string name="mismatchPin" msgid="2929611853228707473">"PIN kodovi koje ste uneli se ne podudaraju."</string>
- <string name="invalidPin" msgid="7542498253319440408">"Otkucajte PIN koji ima od 4 do 8 brojeva."</string>
- <string name="invalidPuk" msgid="8831151490931907083">"Unesite PUK koji se sastoji od 8 cifara ili više."</string>
+ <string name="mmiError" msgid="2862759606579822246">"Проблеми са везом или неважећи MMI кôд."</string>
+ <string name="mmiErrorNotSupported" msgid="5001803469335286099">"Функција није подржана."</string>
+ <string name="mmiFdnError" msgid="3975490266767565852">"Рад је ограничен само на бројеве фиксног бирања."</string>
+ <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"Не можете да промените подешавања преусмеравања позива са телефона док сте у ромингу."</string>
+ <string name="serviceEnabled" msgid="7549025003394765639">"Услуга је омогућена."</string>
+ <string name="serviceEnabledFor" msgid="1463104778656711613">"Услуга је омогућена за:"</string>
+ <string name="serviceDisabled" msgid="641878791205871379">"Услуга је онемогућена."</string>
+ <string name="serviceRegistered" msgid="3856192211729577482">"Регистрација је успела."</string>
+ <string name="serviceErased" msgid="997354043770513494">"Брисање је довршено."</string>
+ <string name="passwordIncorrect" msgid="917087532676155877">"Неисправна лозинка."</string>
+ <string name="mmiComplete" msgid="6341884570892520140">"MMI кôд је извршен."</string>
+ <string name="badPin" msgid="888372071306274355">"Стари PIN који сте унели није тачан."</string>
+ <string name="badPuk" msgid="4232069163733147376">"PUK који сте унели није тачан."</string>
+ <string name="mismatchPin" msgid="2929611853228707473">"PIN кодови које сте унели се не подударају."</string>
+ <string name="invalidPin" msgid="7542498253319440408">"Откуцајте PIN који има од 4 до 8 бројева."</string>
+ <string name="invalidPuk" msgid="8831151490931907083">"Унесите PUK који се састоји од 8 цифара или више."</string>
<!-- no translation found for needPuk (3503414069503752211) -->
<skip />
<!-- no translation found for needPuk2 (3910763547447344963) -->
<skip />
- <string name="enablePin" msgid="2543771964137091212">"Nije uspelo. Omogućite zaključavanje SIM/RUIM kartice."</string>
+ <string name="enablePin" msgid="2543771964137091212">"Није успело. Омогућите закључавање SIM/RUIM картице."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
- <item quantity="one">Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj pre nego što se SIM kartica zaključa.</item>
- <item quantity="few">Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja pre nego što se SIM kartica zaključa.</item>
- <item quantity="other">Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja pre nego što se SIM kartica zaključa.</item>
+ <item quantity="one">Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушај пре него што се SIM картица закључа.</item>
+ <item quantity="few">Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја пре него што се SIM картица закључа.</item>
+ <item quantity="other">Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја пре него што се SIM картица закључа.</item>
</plurals>
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
- <string name="ClipMmi" msgid="4110549342447630629">"Dolazni ID pozivaoca"</string>
- <string name="ClirMmi" msgid="6752346475055446417">"Sakrijte ID odlaznog pozivaoca"</string>
- <string name="ColpMmi" msgid="4736462893284419302">"ID povezane linije"</string>
- <string name="ColrMmi" msgid="5889782479745764278">"Ograničenje ID-a povezane linije"</string>
- <string name="CfMmi" msgid="8390012691099787178">"Preusmeravanje poziva"</string>
- <string name="CwMmi" msgid="3164609577675404761">"Poziv na čekanju"</string>
- <string name="BaMmi" msgid="7205614070543372167">"Ograničavanje poziva"</string>
- <string name="PwdMmi" msgid="3360991257288638281">"Promena lozinke"</string>
- <string name="PinMmi" msgid="7133542099618330959">"Promena PIN koda"</string>
- <string name="CnipMmi" msgid="4897531155968151160">"Pozivanje postojećeg broja"</string>
- <string name="CnirMmi" msgid="885292039284503036">"Pozivanje broja je ograničeno"</string>
- <string name="ThreeWCMmi" msgid="2436550866139999411">"Trosmerno pozivanje"</string>
- <string name="RuacMmi" msgid="1876047385848991110">"Odbijanje nepoželjnih poziva"</string>
- <string name="CndMmi" msgid="185136449405618437">"Isporuka broja za pozivanje"</string>
- <string name="DndMmi" msgid="8797375819689129800">"Ne uznemiravaj"</string>
- <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"ID pozivaoca je podrazumevano ograničen. Sledeći poziv: ograničen."</string>
- <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID pozivaoca je podrazumevano ograničen. Sledeći poziv: Nije ograničen."</string>
- <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID pozivaoca podrazumevano nije ograničen. Sledeći poziv: ograničen."</string>
- <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID pozivaoca podrazumevano nije ograničen. Sledeći poziv: Nije ograničen."</string>
- <string name="serviceNotProvisioned" msgid="8289333510236766193">"Usluga nije dobavljena."</string>
- <string name="CLIRPermanent" msgid="166443681876381118">"Ne možete da promenite podešavanje ID-a korisnika."</string>
- <string name="auto_data_switch_title" msgid="3286350716870518297">"Mobilni podaci su prebačeni na operatera <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
- <string name="auto_data_switch_content" msgid="803557715007110959">"Ovo možete u svakom trenutku da promenite u Podešavanjima"</string>
- <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nema usluge mobilnih podataka"</string>
- <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hitni pozivi nisu dostupni"</string>
- <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nema glasovne usluge"</string>
- <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"Nema glasovne usluge ni hitnih poziva"</string>
- <string name="RestrictedStateContent" msgid="7693575344608618926">"Privremeno isključio mobilni operater"</string>
- <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"Privremeno je isključio mobilni operater za SIM <xliff:g id="SIMNUMBER">%d</xliff:g>"</string>
- <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Povezivanje sa mobilnom mrežom nije uspelo"</string>
- <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Probajte da promenite željenu mrežu. Dodirnite da biste promenili."</string>
- <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Hitni pozivi nisu dostupni"</string>
- <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Ne možete da upućujete hitne pozive preko Wi‑Fi-ja"</string>
- <string name="notification_channel_network_alert" msgid="4788053066033851841">"Obaveštenja"</string>
- <string name="notification_channel_call_forward" msgid="8230490317314272406">"Preusmeravanje poziva"</string>
- <string name="notification_channel_emergency_callback" msgid="54074839059123159">"Režim za hitan povratni poziv"</string>
- <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Status mobilnih podataka"</string>
- <string name="notification_channel_sms" msgid="1243384981025535724">"SMS-ovi"</string>
- <string name="notification_channel_voice_mail" msgid="8457433203106654172">"Poruke govorne pošte"</string>
- <string name="notification_channel_wfc" msgid="9048240466765169038">"Pozivanje preko WiFi mreže"</string>
- <string name="notification_channel_sim" msgid="5098802350325677490">"Status SIM-a"</string>
- <string name="notification_channel_sim_high_prio" msgid="642361929452850928">"Obaveštenja SIM kartice sa statusom „visok prioritet“"</string>
- <string name="peerTtyModeFull" msgid="337553730440832160">"Korisnik zahteva POTPUN režim TTY"</string>
- <string name="peerTtyModeHco" msgid="5626377160840915617">"Korisnik zahteva PRENOS ZVUKA za režim TTY"</string>
- <string name="peerTtyModeVco" msgid="572208600818270944">"Korisnik zahteva PRENOS GLASA za režim TTY"</string>
- <string name="peerTtyModeOff" msgid="2420380956369226583">"Korisnik zahteva ISKLJUČEN režim TTY"</string>
+ <string name="ClipMmi" msgid="4110549342447630629">"Долазни ИД позиваоца"</string>
+ <string name="ClirMmi" msgid="6752346475055446417">"Сакријте ИД одлазног позиваоца"</string>
+ <string name="ColpMmi" msgid="4736462893284419302">"ИД повезане линије"</string>
+ <string name="ColrMmi" msgid="5889782479745764278">"Ограничење ИД-а повезане линије"</string>
+ <string name="CfMmi" msgid="8390012691099787178">"Преусмеравање позива"</string>
+ <string name="CwMmi" msgid="3164609577675404761">"Позив на чекању"</string>
+ <string name="BaMmi" msgid="7205614070543372167">"Ограничавање позива"</string>
+ <string name="PwdMmi" msgid="3360991257288638281">"Промена лозинке"</string>
+ <string name="PinMmi" msgid="7133542099618330959">"Промена PIN кода"</string>
+ <string name="CnipMmi" msgid="4897531155968151160">"Позивање постојећег броја"</string>
+ <string name="CnirMmi" msgid="885292039284503036">"Позивање броја је ограничено"</string>
+ <string name="ThreeWCMmi" msgid="2436550866139999411">"Тросмерно позивање"</string>
+ <string name="RuacMmi" msgid="1876047385848991110">"Одбијање непожељних позива"</string>
+ <string name="CndMmi" msgid="185136449405618437">"Испорука броја за позивање"</string>
+ <string name="DndMmi" msgid="8797375819689129800">"Не узнемиравај"</string>
+ <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"ИД позиваоца је подразумевано ограничен. Следећи позив: ограничен."</string>
+ <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ИД позиваоца је подразумевано ограничен. Следећи позив: Није ограничен."</string>
+ <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ИД позиваоца подразумевано није ограничен. Следећи позив: ограничен."</string>
+ <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ИД позиваоца подразумевано није ограничен. Следећи позив: Није ограничен."</string>
+ <string name="serviceNotProvisioned" msgid="8289333510236766193">"Услуга није добављена."</string>
+ <string name="CLIRPermanent" msgid="166443681876381118">"Не можете да промените подешавање ИД-а корисника."</string>
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилни подаци су пребачени на оператера <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Ово можете у сваком тренутку да промените у Подешавањима"</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>
+ <string name="RestrictedStateContent" msgid="7693575344608618926">"Привремено искључио мобилни оператер"</string>
+ <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"Привремено је искључио мобилни оператер за SIM <xliff:g id="SIMNUMBER">%d</xliff:g>"</string>
+ <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Повезивање са мобилном мрежом није успело"</string>
+ <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Пробајте да промените жељену мрежу. Додирните да бисте променили."</string>
+ <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Хитни позиви нису доступни"</string>
+ <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Не можете да упућујете хитне позиве преко Wi‑Fi-ја"</string>
+ <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_sms" msgid="1243384981025535724">"SMS-ови"</string>
+ <string name="notification_channel_voice_mail" msgid="8457433203106654172">"Поруке говорне поште"</string>
+ <string name="notification_channel_wfc" msgid="9048240466765169038">"Позивање преко WiFi мреже"</string>
+ <string name="notification_channel_sim" msgid="5098802350325677490">"Статус SIM-а"</string>
+ <string name="notification_channel_sim_high_prio" msgid="642361929452850928">"Обавештења SIM картице са статусом „висок приоритет“"</string>
+ <string name="peerTtyModeFull" msgid="337553730440832160">"Корисник захтева ПОТПУН режим TTY"</string>
+ <string name="peerTtyModeHco" msgid="5626377160840915617">"Корисник захтева ПРЕНОС ЗВУКА за режим TTY"</string>
+ <string name="peerTtyModeVco" msgid="572208600818270944">"Корисник захтева ПРЕНОС ГЛАСА за режим TTY"</string>
+ <string name="peerTtyModeOff" msgid="2420380956369226583">"Корисник захтева ИСКЉУЧЕН режим TTY"</string>
<string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
- <string name="serviceClassData" msgid="4148080018967300248">"Podaci"</string>
- <string name="serviceClassFAX" msgid="2561653371698904118">"FAKS"</string>
+ <string name="serviceClassData" msgid="4148080018967300248">"Подаци"</string>
+ <string name="serviceClassFAX" msgid="2561653371698904118">"ФАКС"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
- <string name="serviceClassDataAsync" msgid="2029856900898545984">"Asinhroni podaci"</string>
- <string name="serviceClassDataSync" msgid="7895071363569133704">"Sinhronizovano"</string>
- <string name="serviceClassPacket" msgid="1430642951399303804">"Paket"</string>
- <string name="serviceClassPAD" msgid="6850244583416306321">"PODLOGA"</string>
- <string name="roamingText0" msgid="7793257871609854208">"Indikator rominga je uključen"</string>
- <string name="roamingText1" msgid="5073028598334616445">"Indikator rominga je isključen"</string>
- <string name="roamingText2" msgid="2834048284153110598">"Treperenje indikatora rominga"</string>
- <string name="roamingText3" msgid="831690234035748988">"Izvan komšiluka"</string>
- <string name="roamingText4" msgid="2171252529065590728">"Izvan zgrade"</string>
- <string name="roamingText5" msgid="4294671587635796641">"Roming – željeni sistem"</string>
- <string name="roamingText6" msgid="5536156746637992029">"Roming – dostupni sistem"</string>
- <string name="roamingText7" msgid="1783303085512907706">"Roming – partner"</string>
- <string name="roamingText8" msgid="7774800704373721973">"Roming – premijum partner"</string>
- <string name="roamingText9" msgid="1933460020190244004">"Roming – potpuno funkcionisanje usluge"</string>
- <string name="roamingText10" msgid="7434767033595769499">"Roming – delimično funkcionisanje usluge"</string>
- <string name="roamingText11" msgid="5245687407203281407">"Baner rominga je uključen"</string>
- <string name="roamingText12" msgid="673537506362152640">"Baner rominga je isključen"</string>
- <string name="roamingTextSearching" msgid="5323235489657753486">"Pretraživanje usluge"</string>
- <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Podešavanje pozivanja preko WiFi-a nije uspelo"</string>
+ <string name="serviceClassDataAsync" msgid="2029856900898545984">"Асинхрони подаци"</string>
+ <string name="serviceClassDataSync" msgid="7895071363569133704">"Синхронизовано"</string>
+ <string name="serviceClassPacket" msgid="1430642951399303804">"Пакет"</string>
+ <string name="serviceClassPAD" msgid="6850244583416306321">"ПОДЛОГА"</string>
+ <string name="roamingText0" msgid="7793257871609854208">"Индикатор роминга је укључен"</string>
+ <string name="roamingText1" msgid="5073028598334616445">"Индикатор роминга је искључен"</string>
+ <string name="roamingText2" msgid="2834048284153110598">"Треперење индикатора роминга"</string>
+ <string name="roamingText3" msgid="831690234035748988">"Изван комшилука"</string>
+ <string name="roamingText4" msgid="2171252529065590728">"Изван зграде"</string>
+ <string name="roamingText5" msgid="4294671587635796641">"Роминг – жељени систем"</string>
+ <string name="roamingText6" msgid="5536156746637992029">"Роминг – доступни систем"</string>
+ <string name="roamingText7" msgid="1783303085512907706">"Роминг – партнер"</string>
+ <string name="roamingText8" msgid="7774800704373721973">"Роминг – премијум партнер"</string>
+ <string name="roamingText9" msgid="1933460020190244004">"Роминг – потпуно функционисање услуге"</string>
+ <string name="roamingText10" msgid="7434767033595769499">"Роминг – делимично функционисање услуге"</string>
+ <string name="roamingText11" msgid="5245687407203281407">"Банер роминга је укључен"</string>
+ <string name="roamingText12" msgid="673537506362152640">"Банер роминга је искључен"</string>
+ <string name="roamingTextSearching" msgid="5323235489657753486">"Претраживање услуге"</string>
+ <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Подешавање позивања преко WiFi-а није успело"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"Da biste upućivali pozive i slali poruke preko WiFi-a, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko WiFi-a. (kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"Да бисте упућивали позиве и слали поруке преко WiFi-а, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко WiFi-а. (кôд грешке: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
- <item msgid="4795145070505729156">"Problem u vezi sa registrovanjem pozivanja preko Wi‑Fi-ja kod mobilnog operatera: <xliff:g id="CODE">%1$s</xliff:g>"</item>
+ <item msgid="4795145070505729156">"Проблем у вези са регистровањем позивања преко Wi‑Fi-ја код мобилног оператера: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
<!-- no translation found for wfcSpnFormat_spn (2982505428519096311) -->
<skip />
- <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> pozivanje preko WiFi-a"</string>
- <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – pozivanje preko WiFi-a"</string>
- <string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN poziv"</string>
- <string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN poziv"</string>
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> позивање преко WiFi-а"</string>
+ <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – позивање преко WiFi-а"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN позив"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN позив"</string>
<string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> WiFi"</string>
- <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Pozivanje preko WiFi-a | <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Позивање преко WiFi-а | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Pozivanje preko WiFi-a"</string>
+ <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Позивање преко WiFi-а"</string>
<string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"WiFi"</string>
- <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Pozivanje preko WiFi-a"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Позивање преко WiFi-а"</string>
<string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
- <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Isključeno"</string>
- <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Pozivanje preko WiFi-a"</string>
- <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Poziv preko mobilne mreže"</string>
- <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string>
+ <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Искључено"</string>
+ <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Позивање преко WiFi-а"</string>
+ <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Позив преко мобилне мреже"</string>
+ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само WiFi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> rezervni način za pozivanje"</string>
- <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije prosleđeno"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> резервни начин за позивање"</string>
+ <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
- <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> nakon <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunde/i"</string>
- <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije prosleđeno"</string>
- <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije prosleđeno"</string>
- <string name="fcComplete" msgid="1080909484660507044">"Kôd funkcije je izvršen."</string>
- <string name="fcError" msgid="5325116502080221346">"Problemi sa vezom ili nevažeći kôd funkcije."</string>
- <string name="httpErrorOk" msgid="6206751415788256357">"Potvrdi"</string>
- <string name="httpError" msgid="3406003584150566720">"Došlo je do greške na mreži."</string>
- <string name="httpErrorLookup" msgid="3099834738227549349">"Nije moguće pronaći URL adresu"</string>
- <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"Šema potvrda autentičnosti sajta nije podržana."</string>
- <string name="httpErrorAuth" msgid="469553140922938968">"Nije moguće potvrditi autentičnost."</string>
- <string name="httpErrorProxyAuth" msgid="7229662162030113406">"Potvrda identiteta preko proksi servera nije uspela."</string>
- <string name="httpErrorConnect" msgid="3295081579893205617">"Nije moguće povezati se sa serverom."</string>
- <string name="httpErrorIO" msgid="3860318696166314490">"Nije moguće komunicirati sa serverom. Probajte ponovo kasnije."</string>
- <string name="httpErrorTimeout" msgid="7446272815190334204">"Veza sa serverom je istekla."</string>
- <string name="httpErrorRedirectLoop" msgid="8455757777509512098">"Stranica sadrži previše veza za preusmeravanje sa servera."</string>
- <string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"Protokol nije podržan."</string>
- <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"Nije moguće uspostaviti bezbednu vezu."</string>
- <string name="httpErrorBadUrl" msgid="754447723314832538">"Stranicu nije moguće otvoriti zato što je URL adresa nevažeća."</string>
- <string name="httpErrorFile" msgid="3400658466057744084">"Nije moguće pristupiti datoteci."</string>
- <string name="httpErrorFileNotFound" msgid="5191433324871147386">"Nije moguće pronaći traženu datoteku."</string>
- <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Previše zahteva se obrađuje. Probajte ponovo kasnije."</string>
- <string name="notification_title" msgid="5783748077084481121">"Greška pri prijavljivanju za <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
- <string name="contentServiceSync" msgid="2341041749565687871">"Sinhronizacija"</string>
- <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"Sinhronizacija nije uspela"</string>
- <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"Previše pokušaja brisanja sadržaja <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
- <string name="low_memory" product="tablet" msgid="5557552311566179924">"Memorija tableta je puna! Izbrišite neke datoteke da biste oslobodili prostor."</string>
- <string name="low_memory" product="watch" msgid="3479447988234030194">"Memorija sata je puna. Izbrišite neke datoteke da biste oslobodili prostor."</string>
- <string name="low_memory" product="tv" msgid="6663680413790323318">"Memorijski prostor na Android TV uređaju je pun. Izbrišite neke datoteke da biste oslobodili prostor."</string>
- <string name="low_memory" product="default" msgid="2539532364144025569">"Memorija telefona je puna! Izbrišite neke datoteke da biste oslobodili prostor."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Instaliran je autoritet za izdavanje sertifikata}one{Instalirani su autoriteti za izdavanje sertifikata}few{Instalirani su autoriteti za izdavanje sertifikata}other{Instalirani su autoriteti za izdavanje sertifikata}}"</string>
- <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Od strane nepoznate treće strane"</string>
- <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Od strane administratora poslovnog profila"</string>
- <string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Od strane <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
- <string name="work_profile_deleted" msgid="5891181538182009328">"Poslovni profil je izbrisan"</string>
- <string name="work_profile_deleted_details" msgid="3773706828364418016">"Aplikacija za administratore na poslovnom profilu nedostaje ili je oštećena. Zbog toga su poslovni profil i povezani podaci izbrisani. Obratite se administratoru za pomoć."</string>
- <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Poslovni profil više nije dostupan na ovom uređaju"</string>
- <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Previše pokušaja unosa lozinke"</string>
- <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator je ustupio uređaj za ličnu upotrebu"</string>
- <string name="network_logging_notification_title" msgid="554983187553845004">"Uređajem se upravlja"</string>
- <string name="network_logging_notification_text" msgid="1327373071132562512">"Organizacija upravlja ovim uređajem i može da nadgleda mrežni saobraćaj. Dodirnite za detalje."</string>
- <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije mogu da pristupaju vašoj lokaciji"</string>
- <string name="location_changed_notification_text" msgid="7158423339982706912">"Obratite se IT administratoru da biste saznali više"</string>
- <string name="geofencing_service" msgid="3826902410740315456">"Usluga virtuelnog geografskog opsega"</string>
- <string name="country_detector" msgid="7023275114706088854">"Detektor zemlje"</string>
- <string name="location_service" msgid="2439187616018455546">"Usluga lokacije"</string>
- <string name="gnss_service" msgid="8907781262179951385">"GNSS usluga"</string>
- <string name="sensor_notification_service" msgid="7474531979178682676">"Usluga obaveštenja senzora"</string>
- <string name="twilight_service" msgid="8964898045693187224">"Usluga Sumrak"</string>
- <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS usluga za ažuriranje vremena"</string>
- <string name="device_policy_manager_service" msgid="5085762851388850332">"Usluga Menadžer smernica za uređaje"</string>
- <string name="music_recognition_manager_service" msgid="7481956037950276359">"Usluga Menadžer prepoznavanja muzike"</string>
- <string name="factory_reset_warning" msgid="6858705527798047809">"Uređaj će biti obrisan"</string>
- <string name="factory_reset_message" msgid="2657049595153992213">"Ne možete da koristite ovu aplikaciju za administratore. Uređaj će sada biti obrisan.\n\nAko imate pitanja, kontaktirajte administratora organizacije."</string>
- <string name="printing_disabled_by" msgid="3517499806528864633">"Štampanje je onemogućila aplikacija <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
- <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Uključite poslovni profil"</string>
- <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Lične aplikacije su blokirane dok ne uključite poslovni profil"</string>
- <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Lične aplikacije će biti blokirane: <xliff:g id="DATE">%1$s</xliff:g> u <xliff:g id="TIME">%2$s</xliff:g>. IT administrator ne dozvoljava da poslovni profil bude isključen duže od <xliff:g id="NUMBER">%3$d</xliff:g> dana."</string>
- <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Uključi"</string>
- <string name="me" msgid="6207584824693813140">"Ja"</string>
- <string name="power_dialog" product="tablet" msgid="8333207765671417261">"Opcije za tablet"</string>
- <string name="power_dialog" product="tv" msgid="7792839006640933763">"Opcije Android TV-a"</string>
- <string name="power_dialog" product="default" msgid="1107775420270203046">"Opcije telefona"</string>
- <string name="silent_mode" msgid="8796112363642579333">"Nečujni režim"</string>
- <string name="turn_on_radio" msgid="2961717788170634233">"Uključi bežični signal"</string>
- <string name="turn_off_radio" msgid="7222573978109933360">"Isključi bežični signal"</string>
- <string name="screen_lock" msgid="2072642720826409809">"Zaključaj ekran"</string>
- <string name="power_off" msgid="4111692782492232778">"Ugasi"</string>
- <string name="silent_mode_silent" msgid="5079789070221150912">"Zvono je isključeno"</string>
- <string name="silent_mode_vibrate" msgid="8821830448369552678">"Vibracija zvona"</string>
- <string name="silent_mode_ring" msgid="6039011004781526678">"Zvono je uključeno"</string>
- <string name="reboot_to_update_title" msgid="2125818841916373708">"Android ažuriranje sistema"</string>
- <string name="reboot_to_update_prepare" msgid="6978842143587422365">"Ažuriranje se priprema…"</string>
- <string name="reboot_to_update_package" msgid="4644104795527534811">"Paket ažuriranja se obrađuje..."</string>
- <string name="reboot_to_update_reboot" msgid="4474726009984452312">"Ponovo se pokreće..."</string>
- <string name="reboot_to_reset_title" msgid="2226229680017882787">"Resetovanje na fabrička podešavanja"</string>
- <string name="reboot_to_reset_message" msgid="3347690497972074356">"Ponovo se pokreće..."</string>
- <string name="shutdown_progress" msgid="5017145516412657345">"Isključivanje…"</string>
- <string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"Tablet će se isključiti."</string>
- <string name="shutdown_confirm" product="tv" msgid="7975942887313518330">"Android TV uređaj će se ugasiti."</string>
- <string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"Sat će se ugasiti."</string>
- <string name="shutdown_confirm" product="default" msgid="136816458966692315">"Telefon će se isključiti."</string>
- <string name="shutdown_confirm_question" msgid="796151167261608447">"Da li želite da isključite telefon?"</string>
- <string name="reboot_safemode_title" msgid="5853949122655346734">"Restartuj sistem u bezbednom režimu"</string>
- <string name="reboot_safemode_confirm" msgid="1658357874737219624">"Da li želite da ponovo pokrenete sistem u bezbednom režimu? Ovo će onemogućiti sve instalirane aplikacije nezavisnih proizvođača. One će biti vraćene kada ponovo pokrenete sistem."</string>
- <string name="recent_tasks_title" msgid="8183172372995396653">"Nedavno"</string>
- <string name="no_recent_tasks" msgid="9063946524312275906">"Nema nedavnih aplikacija."</string>
- <string name="global_actions" product="tablet" msgid="4412132498517933867">"Opcije za tablet"</string>
- <string name="global_actions" product="tv" msgid="3871763739487450369">"Opcije Android TV-a"</string>
- <string name="global_actions" product="default" msgid="6410072189971495460">"Opcije telefona"</string>
- <string name="global_action_lock" msgid="6949357274257655383">"Zaključaj ekran"</string>
- <string name="global_action_power_off" msgid="4404936470711393203">"Ugasi"</string>
- <string name="global_action_power_options" msgid="1185286119330160073">"Napajanje"</string>
- <string name="global_action_restart" msgid="4678451019561687074">"Restartuj"</string>
- <string name="global_action_emergency" msgid="1387617624177105088">"Hitan poziv"</string>
- <string name="global_action_bug_report" msgid="5127867163044170003">"Izveštaj o grešci"</string>
- <string name="global_action_logout" msgid="6093581310002476511">"Završi sesiju"</string>
- <string name="global_action_screenshot" msgid="2610053466156478564">"Snimak ekrana"</string>
- <string name="bugreport_title" msgid="8549990811777373050">"Izveštaj o grešci"</string>
- <string name="bugreport_message" msgid="5212529146119624326">"Ovim će se prikupiti informacije o trenutnom stanju uređaja kako bi bile poslate u poruci e-pošte. Od započinjanja izveštaja o grešci do trenutka za njegovo slanje proći će neko vreme; budite strpljivi."</string>
- <string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Interaktiv. izveštaj"</string>
- <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Koristite ovo u većini slučajeva. To vam omogućava da pratite napredak izveštaja, da unosite dodatne detalje o problemu i da snimate snimke ekrana. Verovatno će izostaviti neke manje korišćene odeljke za koje pravljenje izveštaja dugo traje."</string>
- <string name="bugreport_option_full_title" msgid="7681035745950045690">"Kompletan izveštaj"</string>
- <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Koristite ovu opciju radi minimalnih sistemskih smetnji kada uređaj ne reaguje, prespor je ili su vam potrebni svi odeljci izveštaja. Ne dozvoljava vam unos dodatnih detalja niti snimanje dodatnih snimaka ekrana."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Napravićemo snimak ekrana radi izveštaja o grešci za # sekundu.}one{Napravićemo snimak ekrana radi izveštaja o grešci za # sekundu.}few{Napravićemo snimak ekrana radi izveštaja o grešci za # sekunde.}other{Napravićemo snimak ekrana radi izveštaja o grešci za # sekundi.}}"</string>
- <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Ekran sa izveštajem o grešci je snimljen"</string>
- <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Snimanje ekrana sa izveštajem o grešci nije uspelo"</string>
- <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Nečujni režim"</string>
- <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"Zvuk je ISKLJUČEN"</string>
- <string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"Zvuk je UKLJUČEN"</string>
- <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Režim rada u avionu"</string>
- <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"Režim rada u avionu je UKLJUČEN"</string>
- <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"Režim rada u avionu je ISKLJUČEN"</string>
- <string name="global_action_settings" msgid="4671878836947494217">"Podešavanja"</string>
- <string name="global_action_assist" msgid="2517047220311505805">"Pomoć"</string>
- <string name="global_action_voice_assist" msgid="6655788068555086695">"Glasovna pomoć"</string>
- <string name="global_action_lockdown" msgid="2475471405907902963">"Zaključavanje"</string>
+ <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> након <xliff:g id="TIME_DELAY">{2}</xliff:g> секунде/и"</string>
+ <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string>
+ <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string>
+ <string name="fcComplete" msgid="1080909484660507044">"Кôд функције је извршен."</string>
+ <string name="fcError" msgid="5325116502080221346">"Проблеми са везом или неважећи кôд функције."</string>
+ <string name="httpErrorOk" msgid="6206751415788256357">"Потврди"</string>
+ <string name="httpError" msgid="3406003584150566720">"Дошло је до грешке на мрежи."</string>
+ <string name="httpErrorLookup" msgid="3099834738227549349">"Није могуће пронаћи URL адресу"</string>
+ <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"Шема потврда аутентичности сајта није подржана."</string>
+ <string name="httpErrorAuth" msgid="469553140922938968">"Није могуће потврдити аутентичност."</string>
+ <string name="httpErrorProxyAuth" msgid="7229662162030113406">"Потврда идентитета преко прокси сервера није успела."</string>
+ <string name="httpErrorConnect" msgid="3295081579893205617">"Није могуће повезати се са сервером."</string>
+ <string name="httpErrorIO" msgid="3860318696166314490">"Није могуће комуницирати са сервером. Пробајте поново касније."</string>
+ <string name="httpErrorTimeout" msgid="7446272815190334204">"Веза са сервером је истекла."</string>
+ <string name="httpErrorRedirectLoop" msgid="8455757777509512098">"Страница садржи превише веза за преусмеравање са сервера."</string>
+ <string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"Протокол није подржан."</string>
+ <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"Није могуће успоставити безбедну везу."</string>
+ <string name="httpErrorBadUrl" msgid="754447723314832538">"Страницу није могуће отворити зато што је URL адреса неважећа."</string>
+ <string name="httpErrorFile" msgid="3400658466057744084">"Није могуће приступити датотеци."</string>
+ <string name="httpErrorFileNotFound" msgid="5191433324871147386">"Није могуће пронаћи тражену датотеку."</string>
+ <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Превише захтева се обрађује. Пробајте поново касније."</string>
+ <string name="notification_title" msgid="5783748077084481121">"Грешка при пријављивању за <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
+ <string name="contentServiceSync" msgid="2341041749565687871">"Синхронизација"</string>
+ <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"Синхронизација није успела"</string>
+ <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"Превише покушаја брисања садржаја <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
+ <string name="low_memory" product="tablet" msgid="5557552311566179924">"Меморија таблета је пуна! Избришите неке датотеке да бисте ослободили простор."</string>
+ <string name="low_memory" product="watch" msgid="3479447988234030194">"Меморија сата је пуна. Избришите неке датотеке да бисте ослободили простор."</string>
+ <string name="low_memory" product="tv" msgid="6663680413790323318">"Меморијски простор на Android TV уређају је пун. Избришите неке датотеке да бисте ослободили простор."</string>
+ <string name="low_memory" product="default" msgid="2539532364144025569">"Меморија телефона је пуна! Избришите неке датотеке да бисте ослободили простор."</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Инсталиран је ауторитет за издавање сертификата}one{Инсталирани су ауторитети за издавање сертификата}few{Инсталирани су ауторитети за издавање сертификата}other{Инсталирани су ауторитети за издавање сертификата}}"</string>
+ <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Од стране непознате треће стране"</string>
+ <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Од стране администратора пословног профила"</string>
+ <string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Од стране <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
+ <string name="work_profile_deleted" msgid="5891181538182009328">"Пословни профил је избрисан"</string>
+ <string name="work_profile_deleted_details" msgid="3773706828364418016">"Апликација за администраторе на пословном профилу недостаје или је оштећена. Због тога су пословни профил и повезани подаци избрисани. Обратите се администратору за помоћ."</string>
+ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Пословни профил више није доступан на овом уређају"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Превише покушаја уноса лозинке"</string>
+ <string name="device_ownership_relinquished" msgid="4080886992183195724">"Администратор је уступио уређај за личну употребу"</string>
+ <string name="network_logging_notification_title" msgid="554983187553845004">"Уређајем се управља"</string>
+ <string name="network_logging_notification_text" msgid="1327373071132562512">"Организација управља овим уређајем и може да надгледа мрежни саобраћај. Додирните за детаље."</string>
+ <string name="location_changed_notification_title" msgid="3620158742816699316">"Апликације могу да приступају вашој локацији"</string>
+ <string name="location_changed_notification_text" msgid="7158423339982706912">"Обратите се ИТ администратору да бисте сазнали више"</string>
+ <string name="geofencing_service" msgid="3826902410740315456">"Услуга виртуелног географског опсега"</string>
+ <string name="country_detector" msgid="7023275114706088854">"Детектор земље"</string>
+ <string name="location_service" msgid="2439187616018455546">"Услуга локације"</string>
+ <string name="gnss_service" msgid="8907781262179951385">"GNSS услуга"</string>
+ <string name="sensor_notification_service" msgid="7474531979178682676">"Услуга обавештења сензора"</string>
+ <string name="twilight_service" msgid="8964898045693187224">"Услуга Сумрак"</string>
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS услуга за ажурирање времена"</string>
+ <string name="device_policy_manager_service" msgid="5085762851388850332">"Услуга Менаџер смерница за уређаје"</string>
+ <string name="music_recognition_manager_service" msgid="7481956037950276359">"Услуга Менаџер препознавања музике"</string>
+ <string name="factory_reset_warning" msgid="6858705527798047809">"Уређај ће бити обрисан"</string>
+ <string name="factory_reset_message" msgid="2657049595153992213">"Не можете да користите ову апликацију за администраторе. Уређај ће сада бити обрисан.\n\nАко имате питања, контактирајте администратора организације."</string>
+ <string name="printing_disabled_by" msgid="3517499806528864633">"Штампање је онемогућила апликација <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
+ <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Укључите пословни профил"</string>
+ <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Личне апликације су блокиране док не укључите пословни профил"</string>
+ <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Личне апликације ће бити блокиране: <xliff:g id="DATE">%1$s</xliff:g> у <xliff:g id="TIME">%2$s</xliff:g>. ИТ администратор не дозвољава да пословни профил буде искључен дуже од <xliff:g id="NUMBER">%3$d</xliff:g> дана."</string>
+ <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Укључи"</string>
+ <string name="me" msgid="6207584824693813140">"Ја"</string>
+ <string name="power_dialog" product="tablet" msgid="8333207765671417261">"Опције за таблет"</string>
+ <string name="power_dialog" product="tv" msgid="7792839006640933763">"Опције Android TV-а"</string>
+ <string name="power_dialog" product="default" msgid="1107775420270203046">"Опције телефона"</string>
+ <string name="silent_mode" msgid="8796112363642579333">"Нечујни режим"</string>
+ <string name="turn_on_radio" msgid="2961717788170634233">"Укључи бежични сигнал"</string>
+ <string name="turn_off_radio" msgid="7222573978109933360">"Искључи бежични сигнал"</string>
+ <string name="screen_lock" msgid="2072642720826409809">"Закључај екран"</string>
+ <string name="power_off" msgid="4111692782492232778">"Угаси"</string>
+ <string name="silent_mode_silent" msgid="5079789070221150912">"Звоно је искључено"</string>
+ <string name="silent_mode_vibrate" msgid="8821830448369552678">"Вибрација звона"</string>
+ <string name="silent_mode_ring" msgid="6039011004781526678">"Звоно је укључено"</string>
+ <string name="reboot_to_update_title" msgid="2125818841916373708">"Android ажурирање система"</string>
+ <string name="reboot_to_update_prepare" msgid="6978842143587422365">"Ажурирање се припрема…"</string>
+ <string name="reboot_to_update_package" msgid="4644104795527534811">"Пакет ажурирања се обрађује..."</string>
+ <string name="reboot_to_update_reboot" msgid="4474726009984452312">"Поново се покреће..."</string>
+ <string name="reboot_to_reset_title" msgid="2226229680017882787">"Ресетовање на фабричка подешавања"</string>
+ <string name="reboot_to_reset_message" msgid="3347690497972074356">"Поново се покреће..."</string>
+ <string name="shutdown_progress" msgid="5017145516412657345">"Искључивање…"</string>
+ <string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"Таблет ће се искључити."</string>
+ <string name="shutdown_confirm" product="tv" msgid="7975942887313518330">"Android TV уређај ће се угасити."</string>
+ <string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"Сат ће се угасити."</string>
+ <string name="shutdown_confirm" product="default" msgid="136816458966692315">"Телефон ће се искључити."</string>
+ <string name="shutdown_confirm_question" msgid="796151167261608447">"Да ли желите да искључите телефон?"</string>
+ <string name="reboot_safemode_title" msgid="5853949122655346734">"Рестартуј систем у безбедном режиму"</string>
+ <string name="reboot_safemode_confirm" msgid="1658357874737219624">"Да ли желите да поново покренете систем у безбедном режиму? Ово ће онемогућити све инсталиране апликације независних произвођача. Оне ће бити враћене када поново покренете систем."</string>
+ <string name="recent_tasks_title" msgid="8183172372995396653">"Недавно"</string>
+ <string name="no_recent_tasks" msgid="9063946524312275906">"Нема недавних апликација."</string>
+ <string name="global_actions" product="tablet" msgid="4412132498517933867">"Опције за таблет"</string>
+ <string name="global_actions" product="tv" msgid="3871763739487450369">"Опције Android TV-а"</string>
+ <string name="global_actions" product="default" msgid="6410072189971495460">"Опције телефона"</string>
+ <string name="global_action_lock" msgid="6949357274257655383">"Закључај екран"</string>
+ <string name="global_action_power_off" msgid="4404936470711393203">"Угаси"</string>
+ <string name="global_action_power_options" msgid="1185286119330160073">"Напајање"</string>
+ <string name="global_action_restart" msgid="4678451019561687074">"Рестартуј"</string>
+ <string name="global_action_emergency" msgid="1387617624177105088">"Хитан позив"</string>
+ <string name="global_action_bug_report" msgid="5127867163044170003">"Извештај о грешци"</string>
+ <string name="global_action_logout" msgid="6093581310002476511">"Заврши сесију"</string>
+ <string name="global_action_screenshot" msgid="2610053466156478564">"Снимак екрана"</string>
+ <string name="bugreport_title" msgid="8549990811777373050">"Извештај о грешци"</string>
+ <string name="bugreport_message" msgid="5212529146119624326">"Овим ће се прикупити информације о тренутном стању уређаја како би биле послате у поруци е-поште. Од започињања извештаја о грешци до тренутка за његово слање проћи ће неко време; будите стрпљиви."</string>
+ <string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Интерактив. извештај"</string>
+ <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Користите ово у већини случајева. То вам омогућава да пратите напредак извештаја, да уносите додатне детаље о проблему и да снимате снимке екрана. Вероватно ће изоставити неке мање коришћене одељке за које прављење извештаја дуго траје."</string>
+ <string name="bugreport_option_full_title" msgid="7681035745950045690">"Комплетан извештај"</string>
+ <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Користите ову опцију ради минималних системских сметњи када уређај не реагује, преспор је или су вам потребни сви одељци извештаја. Не дозвољава вам унос додатних детаља нити снимање додатних снимака екрана."</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Направићемо снимак екрана ради извештаја о грешци за # секунду.}one{Направићемо снимак екрана ради извештаја о грешци за # секунду.}few{Направићемо снимак екрана ради извештаја о грешци за # секунде.}other{Направићемо снимак екрана ради извештаја о грешци за # секунди.}}"</string>
+ <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Екран са извештајем о грешци је снимљен"</string>
+ <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Снимање екрана са извештајем о грешци није успело"</string>
+ <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Нечујни режим"</string>
+ <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"Звук је ИСКЉУЧЕН"</string>
+ <string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"Звук је УКЉУЧЕН"</string>
+ <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Режим рада у авиону"</string>
+ <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"Режим рада у авиону је УКЉУЧЕН"</string>
+ <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"Режим рада у авиону је ИСКЉУЧЕН"</string>
+ <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="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
- <string name="notification_hidden_text" msgid="2835519769868187223">"Novo obaveštenje"</string>
- <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizička tastatura"</string>
- <string name="notification_channel_security" msgid="8516754650348238057">"Bezbednost"</string>
- <string name="notification_channel_car_mode" msgid="2123919247040988436">"Režim rada u automobilu"</string>
- <string name="notification_channel_account" msgid="6436294521740148173">"Status naloga"</string>
- <string name="notification_channel_developer" msgid="1691059964407549150">"Poruke za programere"</string>
- <string name="notification_channel_developer_important" msgid="7197281908918789589">"Važne poruke programera"</string>
- <string name="notification_channel_updates" msgid="7907863984825495278">"Ažuriranja"</string>
- <string name="notification_channel_network_status" msgid="2127687368725272809">"Status mreže"</string>
- <string name="notification_channel_network_alerts" msgid="6312366315654526528">"Obaveštenja u vezi sa mrežom"</string>
- <string name="notification_channel_network_available" msgid="6083697929214165169">"Mreža je dostupna"</string>
- <string name="notification_channel_vpn" msgid="1628529026203808999">"Status VPN-a"</string>
- <string name="notification_channel_device_admin" msgid="6384932669406095506">"Obaveštenja od IT administratora"</string>
- <string name="notification_channel_alerts" msgid="5070241039583668427">"Obaveštenja"</string>
- <string name="notification_channel_retail_mode" msgid="3732239154256431213">"Režim demonstracije za maloprodajne objekte"</string>
- <string name="notification_channel_usb" msgid="1528280969406244896">"USB veza"</string>
- <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aktivna aplikacija"</string>
- <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije koje troše bateriju"</string>
- <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Uvećanje"</string>
- <string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"Korišćenje Pristupačnosti"</string>
- <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> koristi bateriju"</string>
- <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Aplikacije (<xliff:g id="NUMBER">%1$d</xliff:g>) koriste bateriju"</string>
- <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dodirnite za detalje o bateriji i potrošnji podataka"</string>
+ <string name="notification_hidden_text" msgid="2835519769868187223">"Ново обавештење"</string>
+ <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физичка тастатура"</string>
+ <string name="notification_channel_security" msgid="8516754650348238057">"Безбедност"</string>
+ <string name="notification_channel_car_mode" msgid="2123919247040988436">"Режим рада у аутомобилу"</string>
+ <string name="notification_channel_account" msgid="6436294521740148173">"Статус налога"</string>
+ <string name="notification_channel_developer" msgid="1691059964407549150">"Поруке за програмере"</string>
+ <string name="notification_channel_developer_important" msgid="7197281908918789589">"Важне поруке програмера"</string>
+ <string name="notification_channel_updates" msgid="7907863984825495278">"Ажурирања"</string>
+ <string name="notification_channel_network_status" msgid="2127687368725272809">"Статус мреже"</string>
+ <string name="notification_channel_network_alerts" msgid="6312366315654526528">"Обавештења у вези са мрежом"</string>
+ <string name="notification_channel_network_available" msgid="6083697929214165169">"Мрежа је доступна"</string>
+ <string name="notification_channel_vpn" msgid="1628529026203808999">"Статус VPN-а"</string>
+ <string name="notification_channel_device_admin" msgid="6384932669406095506">"Обавештења од ИТ администратора"</string>
+ <string name="notification_channel_alerts" msgid="5070241039583668427">"Обавештења"</string>
+ <string name="notification_channel_retail_mode" msgid="3732239154256431213">"Режим демонстрације за малопродајне објекте"</string>
+ <string name="notification_channel_usb" msgid="1528280969406244896">"USB веза"</string>
+ <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Активна апликација"</string>
+ <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Апликације које троше батерију"</string>
+ <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Увећање"</string>
+ <string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"Коришћење Приступачности"</string>
+ <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> користи батерију"</string>
+ <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Апликације (<xliff:g id="NUMBER">%1$d</xliff:g>) користе батерију"</string>
+ <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Додирните за детаље о батерији и потрошњи података"</string>
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
- <string name="safeMode" msgid="8974401416068943888">"Bezbedni režim"</string>
- <string name="android_system_label" msgid="5974767339591067210">"Android sistem"</string>
- <string name="user_owner_label" msgid="8628726904184471211">"Pređi na lični profil"</string>
- <string name="managed_profile_label" msgid="7316778766973512382">"Pređi na poslovni profil"</string>
- <string name="permgrouplab_contacts" msgid="4254143639307316920">"Kontakti"</string>
- <string name="permgroupdesc_contacts" msgid="9163927941244182567">"pristupi kontaktima"</string>
- <string name="permgrouplab_location" msgid="1858277002233964394">"Lokacija"</string>
- <string name="permgroupdesc_location" msgid="1995955142118450685">"pristupi lokaciji ovog uređaja"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"Kalendar"</string>
- <string name="permgroupdesc_calendar" msgid="6762751063361489379">"pristupi kalendaru"</string>
+ <string name="safeMode" msgid="8974401416068943888">"Безбедни режим"</string>
+ <string name="android_system_label" msgid="5974767339591067210">"Android систем"</string>
+ <string name="user_owner_label" msgid="8628726904184471211">"Пређи на лични профил"</string>
+ <string name="managed_profile_label" msgid="7316778766973512382">"Пређи на пословни профил"</string>
+ <string name="permgrouplab_contacts" msgid="4254143639307316920">"Контакти"</string>
+ <string name="permgroupdesc_contacts" msgid="9163927941244182567">"приступи контактима"</string>
+ <string name="permgrouplab_location" msgid="1858277002233964394">"Локација"</string>
+ <string name="permgroupdesc_location" msgid="1995955142118450685">"приступи локацији овог уређаја"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"Календар"</string>
+ <string name="permgroupdesc_calendar" msgid="6762751063361489379">"приступи календару"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="5726462398070064542">"šalje i pregleda SMS poruke"</string>
- <string name="permgrouplab_storage" msgid="17339216290379241">"Fajlovi"</string>
- <string name="permgroupdesc_storage" msgid="5378659041354582769">"pristup fajlovima na uređaju"</string>
- <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"Muzika i zvuk"</string>
- <string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"pristup muzici i audio sadržaju na uređaju"</string>
- <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"Slike i video snimci"</string>
- <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"pristup slikama i video snimcima na uređaju"</string>
- <string name="permgrouplab_microphone" msgid="2480597427667420076">"Mikrofon"</string>
- <string name="permgroupdesc_microphone" msgid="1047786732792487722">"snima zvuk"</string>
- <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Fizičke aktivnosti"</string>
- <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"pristup fizičkim aktivnostima"</string>
- <string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
- <string name="permgroupdesc_camera" msgid="7585150538459320326">"snima slike i video"</string>
- <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Uređaji u blizini"</string>
- <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"otkrivanje uređaja u blizini i povezivanje sa njima"</string>
- <string name="permgrouplab_calllog" msgid="7926834372073550288">"Evidencije poziva"</string>
- <string name="permgroupdesc_calllog" msgid="2026996642917801803">"čitanje i pisanje evidencije poziva na telefonu"</string>
- <string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string>
- <string name="permgroupdesc_phone" msgid="270048070781478204">"upućuje telefonske pozive i upravlja njima"</string>
- <string name="permgrouplab_sensors" msgid="9134046949784064495">"Senzori za telo"</string>
- <string name="permgroupdesc_sensors" msgid="2610631290633747752">"pristupa podacima senzora o vitalnim funkcijama"</string>
- <string name="permgrouplab_notifications" msgid="5472972361980668884">"Obaveštenja"</string>
- <string name="permgroupdesc_notifications" msgid="4608679556801506580">"prikazivanje obaveštenja"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"da preuzima sadržaj prozora"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Proverava sadržaj prozora sa kojim ostvarujete interakciju."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"da uključi Istraživanja dodirom"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Stavke koje dodirnete će biti izgovorene naglas, a možete da se krećete po ekranu pokretima."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"da prati tekst koji unosite"</string>
- <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Obuhvata lične podatke kao što su brojevi kreditnih kartica i lozinke."</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"da upravlja uvećanjem prikaza"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Upravlja nivoom zumiranja prikaza i određivanjem položaja."</string>
- <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Obavljanje pokreta"</string>
- <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Može da dodiruje, lista, skuplja prikaz i obavlja druge pokrete."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Pokreti za otisak prsta"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Može da registruje pokrete na senzoru za otisak prsta na uređaju."</string>
- <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Napravi snimak ekrana"</string>
- <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Može da napravi snimak ekrana."</string>
- <string name="permlab_statusBar" msgid="8798267849526214017">"onemogućavanje ili izmena statusne trake"</string>
- <string name="permdesc_statusBar" msgid="5809162768651019642">"Dozvoljava aplikaciji da onemogući statusnu traku ili da dodaje i uklanja sistemske ikone."</string>
- <string name="permlab_statusBarService" msgid="2523421018081437981">"funkcionisanje kao statusna traka"</string>
- <string name="permdesc_statusBarService" msgid="6652917399085712557">"Dozvoljava aplikaciji da funkcioniše kao statusna traka."</string>
- <string name="permlab_expandStatusBar" msgid="1184232794782141698">"proširenje/skupljanje statusne trake"</string>
- <string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Dozvoljava aplikaciji da proširuje ili skuplja statusnu traku."</string>
- <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"prikazuje obaveštenja kao aktivnosti preko celog ekrana na zaključanom uređaju"</string>
- <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Omogućava aplikaciji da na zaključanom uređaju prikazuje obaveštenja kao aktivnosti preko celog ekrana."</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instaliranje prečica"</string>
- <string name="permdesc_install_shortcut" msgid="4476328467240212503">"da dodaju prečice na početni ekran bez intervencije korisnika."</string>
- <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"deinstaliranje prečica"</string>
- <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Omogućava aplikaciji da uklanja prečice sa početnog ekrana bez intervencije korisnika."</string>
- <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"preusmeravanje odlaznih poziva"</string>
- <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Dozvoljava aplikaciji da vidi koji broj se bira pri odlaznom pozivu uz opciju da preusmeri poziv na drugi broj ili ga potpuno prekine."</string>
- <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"odgovaraj na telefonske pozive"</string>
- <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Dozvoljava aplikaciji da odgovori na dolazni telefonski poziv."</string>
- <string name="permlab_receiveSms" msgid="505961632050451881">"prijem tekstualnih poruka (SMS)"</string>
- <string name="permdesc_receiveSms" msgid="1797345626687832285">"Dozvoljava aplikaciji da prima i obrađuje SMS poruke. To znači da aplikacija može da nadgleda ili briše poruke koje se šalju uređaju, a da vam ih ne prikaže."</string>
- <string name="permlab_receiveMms" msgid="4000650116674380275">"prijem tekstualnih poruka (MMS)"</string>
- <string name="permdesc_receiveMms" msgid="958102423732219710">"Dozvoljava aplikaciji da prima i obrađuje MMS poruke. To znači da aplikacija može da nadgleda ili briše poruke koje se šalju uređaju, a da vam ih ne prikaže."</string>
- <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Prosleđivanje poruka za mobilne uređaje na lokalitetu"</string>
- <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Dozvoljava aplikaciji da se vezuje za modul poruka za mobilne uređaje na lokalitetu da bi prosleđivala poruke za mobilne uređaje na lokalitetu onako kako su primljene. Obaveštenja poruka za mobilne uređaje na lokalitetu se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na performanse ili ometaju rad uređaja kada se primi poruka o hitnom slučaju za mobilne uređaje na lokalitetu."</string>
- <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Upravljanje odlaznim pozivima"</string>
- <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Omogućava aplikaciji da vidi detalje o odlaznim pozivima na uređaju i da kontroliše te pozive."</string>
- <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"čitanje poruka info servisa"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Omogućava aplikaciji da čita poruke info servisa koje uređaj prima. Upozorenja info servisa se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na performanse ili ometaju funkcionisanje uređaja kada se primi poruka info servisa o hitnom slučaju."</string>
- <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"čitanje prijavljenih fidova"</string>
- <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Dozvoljava aplikaciji da preuzima detalje o trenutno sinhronizovanim fidovima."</string>
- <string name="permlab_sendSms" msgid="7757368721742014252">"šalje i pregleda SMS poruke"</string>
- <string name="permdesc_sendSms" msgid="6757089798435130769">"Dozvoljava aplikaciji da šalje SMS poruke. Ovo može da dovede do neočekivanih troškova. Zlonamerne aplikacije mogu da šalju poruke bez vaše potvrde, što može da izazove troškove."</string>
- <string name="permlab_readSms" msgid="5164176626258800297">"čitanje tekstualnih poruka (SMS ili MMS)"</string>
- <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Ova aplikacija može da čita sve SMS (tekstualne) poruke koje se čuvaju na tabletu."</string>
- <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Ova aplikacija može da čita sve SMS (tekstualne) poruke koje se čuvaju na Android TV uređaju."</string>
- <string name="permdesc_readSms" product="default" msgid="774753371111699782">"Ova aplikacija može da čita sve SMS (tekstualne) poruke koje se čuvaju na telefonu."</string>
- <string name="permlab_receiveWapPush" msgid="4223747702856929056">"prijem tekstualnih poruka (WAP)"</string>
- <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Dozvoljava aplikaciji da prima i obrađuje WAP poruke. Ova dozvola uključuje mogućnost praćenja ili brisanja poruka koje vam se šalju, a koje vam se ne prikazuju."</string>
- <string name="permlab_getTasks" msgid="7460048811831750262">"preuzimanje pokrenutih aplikacija"</string>
- <string name="permdesc_getTasks" msgid="7388138607018233726">"Dozvoljava aplikaciji da preuzima informacije o aktuelnim i nedavno pokrenutim zadacima. Ovo može da omogući aplikaciji da otkrije informacije o tome koje se aplikacije koriste na uređaju."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"upravljanje vlasnicima profila i uređaja"</string>
- <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"Dozvoljava aplikaciji da podesi vlasnike profila i vlasnika uređaja."</string>
- <string name="permlab_reorderTasks" msgid="7598562301992923804">"promena redosleda pokrenutih aplikacija"</string>
- <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Dozvoljava aplikaciji da premešta zadatke u prvi plan i u pozadinu. Aplikacija može da radi ovo bez vašeg unosa."</string>
- <string name="permlab_enableCarMode" msgid="893019409519325311">"omogućavanje režima rada u automobilu"</string>
- <string name="permdesc_enableCarMode" msgid="56419168820473508">"Dozvoljava aplikaciji da omogući režim rada u automobilu."</string>
- <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"zatvaranje drugih aplikacija"</string>
- <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"Dozvoljava aplikaciji da zaustavi pozadinske procese drugih aplikacija. Ovo može da zaustavi druge aplikacije."</string>
- <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"Ova aplikacija može da se prikazuje preko drugih aplikacija"</string>
- <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"Ova aplikacija može da se prikazuje preko drugih aplikacija ili drugih delova delova ekrana. To može da ometa standardno korišćenje aplikacija i način na koji se druge aplikacije prikazuju."</string>
- <string name="permlab_runInBackground" msgid="541863968571682785">"pokretanje u pozadini"</string>
- <string name="permdesc_runInBackground" msgid="4344539472115495141">"Ova aplikacija može da se pokreće u pozadini. To može brže da istroši bateriju."</string>
- <string name="permlab_useDataInBackground" msgid="783415807623038947">"korišćenje podataka u pozadini"</string>
- <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"Ova aplikacija može da koristi podatke u pozadini. To može da poveća potrošnju podataka."</string>
- <string name="permlab_schedule_exact_alarm" msgid="6683283918033029730">"Zakazivanje vremenski preciznih radnji"</string>
- <string name="permdesc_schedule_exact_alarm" msgid="8198009212013211497">"Ova aplikacija može da zakaže da se rad dogodi u željeno vreme u budućnosti. To znači i da aplikacija može da radi kada ne koristite aktivno uređaj."</string>
- <string name="permlab_use_exact_alarm" msgid="348045139777131552">"Zakazivanje alarma ili podsetnika za događaje"</string>
- <string name="permdesc_use_exact_alarm" msgid="7033761461886938912">"Ova aplikacija može da zakazuje radnje poput alarma i podsetnika da bi vas obaveštavala u željeno vreme u budućnosti."</string>
- <string name="permlab_persistentActivity" msgid="464970041740567970">"omogućavanje neprekidne aktivnosti aplikacije"</string>
- <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Dozvoljava aplikaciji da učini sopstvene komponente trajnim u memoriji. Ovo može da ograniči memoriju dostupnu drugim aplikacijama i uspori tablet."</string>
- <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Dozvoljava aplikaciji da trajno zadrži neke svoje delove u memoriji. Ovo može da ograniči memoriju dostupnu drugim aplikacijama i uspori Android TV uređaj."</string>
- <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Dozvoljava aplikaciji da učini sopstvene komponente trajnim u memoriji. Ovo može da ograniči memoriju dostupnu drugim aplikacijama i uspori telefon."</string>
- <string name="permlab_foregroundService" msgid="1768855976818467491">"pokreni uslugu u prvom planu"</string>
- <string name="permdesc_foregroundService" msgid="8720071450020922795">"Dozvoljava aplikaciji da koristi usluge u prvom planu."</string>
- <string name="permlab_foregroundServiceCamera" msgid="7814751737955715297">"pokretanje usluge u prvom planu koja pripada tipu „camera“"</string>
- <string name="permdesc_foregroundServiceCamera" msgid="6973701931250595727">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „camera“"</string>
- <string name="permlab_foregroundServiceConnectedDevice" msgid="3019650546176872501">"pokretanje usluge u prvom planu koja pripada tipu „connectedDevice“"</string>
- <string name="permdesc_foregroundServiceConnectedDevice" msgid="1067457315741352963">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „connectedDevice“"</string>
- <string name="permlab_foregroundServiceDataSync" msgid="5847463514326881076">"pokretanje usluge u prvom planu koja pripada tipu „dataSync“"</string>
- <string name="permdesc_foregroundServiceDataSync" msgid="2267140263423973050">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „dataSync“"</string>
- <string name="permlab_foregroundServiceLocation" msgid="3745428302378535690">"pokretanje usluge u prvom planu koja pripada tipu „location“"</string>
- <string name="permdesc_foregroundServiceLocation" msgid="118894034365177183">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „location“"</string>
- <string name="permlab_foregroundServiceMediaPlayback" msgid="4002687983891935514">"pokretanje usluge u prvom planu koja pripada tipu „mediaPlayback“"</string>
- <string name="permdesc_foregroundServiceMediaPlayback" msgid="3638032446063968043">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „mediaPlayback“"</string>
- <string name="permlab_foregroundServiceMediaProjection" msgid="2630868915733312527">"pokretanje usluge u prvom planu koja pripada tipu „mediaProjection“"</string>
- <string name="permdesc_foregroundServiceMediaProjection" msgid="4805677128082002298">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „mediaProjection“"</string>
- <string name="permlab_foregroundServiceMicrophone" msgid="7390033424890545399">"pokretanje usluge u prvom planu koja pripada tipu „microphone“"</string>
- <string name="permdesc_foregroundServiceMicrophone" msgid="1206041516173483201">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „microphone“"</string>
- <string name="permlab_foregroundServicePhoneCall" msgid="627937743867697892">"pokretanje usluge u prvom planu koja pripada tipu „phoneCall“"</string>
- <string name="permdesc_foregroundServicePhoneCall" msgid="5941660252587015147">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „phoneCall“"</string>
- <string name="permlab_foregroundServiceHealth" msgid="3675776442080928184">"pokretanje usluge u prvom planu koja pripada tipu „health“"</string>
- <string name="permdesc_foregroundServiceHealth" msgid="2024586220562667185">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „health“"</string>
- <string name="permlab_foregroundServiceRemoteMessaging" msgid="105670277002780950">"pokretanje usluge u prvom planu koja pripada tipu „remoteMessaging“"</string>
- <string name="permdesc_foregroundServiceRemoteMessaging" msgid="8767598075877576277">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „remoteMessaging“"</string>
- <string name="permlab_foregroundServiceSystemExempted" msgid="1597663713590612685">"pokretanje usluge u prvom planu koja pripada tipu „systemExempted“"</string>
- <string name="permdesc_foregroundServiceSystemExempted" msgid="947381760834649622">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „systemExempted“"</string>
- <string name="permlab_foregroundServiceFileManagement" msgid="2585000987966045030">"pokretanje usluge u prvom planu koja pripada tipu „fileManagement“"</string>
- <string name="permdesc_foregroundServiceFileManagement" msgid="417103601269698508">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „fileManagement“"</string>
- <string name="permlab_foregroundServiceSpecialUse" msgid="7973536745876645082">"pokretanje usluge u prvom planu koja pripada tipu „specialUse“"</string>
- <string name="permdesc_foregroundServiceSpecialUse" msgid="646713654541885919">"Dozvoljava aplikaciji da koristi usluge u prvom planu koje pripadaju tipu „specialUse“"</string>
- <string name="permlab_getPackageSize" msgid="375391550792886641">"merenje memorijskog prostora u aplikaciji"</string>
- <string name="permdesc_getPackageSize" msgid="742743530909966782">"Dozvoljava aplikaciji da preuzme veličine kôda, podataka i keša."</string>
- <string name="permlab_writeSettings" msgid="8057285063719277394">"izmena podešavanja sistema"</string>
- <string name="permdesc_writeSettings" msgid="8293047411196067188">"Dozvoljava aplikaciji da menja podatke o podešavanju sistema. Zlonamerne aplikacije mogu da oštete konfiguraciju sistema."</string>
- <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"pokretanje pri pokretanju sistema"</string>
- <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Omogućava da se aplikacija pokrene odmah nakon pokretanja sistema. To može da uspori pokretanje tableta, pri čemu ova aplikacija može da uspori funkcionisanje celog tableta time što će uvek biti aktivna."</string>
- <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Dozvoljava aplikaciji da se pokrene odmah po uključivanju sistema. To može da uspori pokretanje Android TV uređaja i aplikacija može da uspori funkcionisanje uređaja u celini tako što će uvek biti aktivna."</string>
- <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Omogućava da se aplikacija pokrene čim se sistem pokrene. To može da uspori pokretanje telefona, pri čemu ova aplikacija može da uspori funkcionisanje celog telefona time što će uvek biti aktivna."</string>
- <string name="permlab_broadcastSticky" msgid="4552241916400572230">"slanje prijemčivih emitovanja"</string>
- <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Dozvoljava aplikaciji da šalje prijemčiva emitovanja, koja ostaju po završetku emitovanja. Prekomerna upotreba može da uspori ili destabilizuje tablet tako što će ga primorati da troši previše memorije."</string>
- <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Dozvoljava aplikaciji da šalje lepljiva emitovanja koja ostaju po završetku emitovanja. Prekomerna upotreba može da uspori ili destabilizuje Android TV uređaj tako što će ga primorati da troši previše memorije."</string>
- <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Dozvoljava aplikaciji da šalje prijemčiva emitovanja, koja ostaju po završetku emitovanja. Prekomerna upotreba može da uspori ili destabilizuje telefon tako što će ga primorati da troši previše memorije."</string>
- <string name="permlab_readContacts" msgid="8776395111787429099">"čitanje kontakata"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Dozvoljava aplikaciji da čita podatke o kontaktima koje čuvate na tabletu. Aplikacije će imati pristup i nalozima na vašem tabletu na kojima su napravljeni kontakti. Tu mogu da spadaju nalozi koje su otvorile aplikacije koje ste instalirali. Ova dozvola omogućava aplikacijama da čuvaju podatke o kontaktima i zlonamerne aplikacije mogu da dele podatke o kontaktima bez vašeg znanja."</string>
- <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Dozvoljava aplikaciji da čita podatke o kontaktima koje čuvate na Android TV uređaju. Aplikacije će imati pristup i nalozima na vašem Android TV uređaju na kojima su napravljeni kontakti. Tu mogu da spadaju nalozi koje su otvorile aplikacije koje ste instalirali. Ova dozvola omogućava aplikacijama da čuvaju podatke o kontaktima i zlonamerne aplikacije mogu da dele podatke o kontaktima bez vašeg znanja."</string>
- <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Dozvoljava aplikaciji da čita podatke o kontaktima koje čuvate na telefonu. Aplikacije će imati pristup i nalozima na vašem telefonu na kojima su napravljeni kontakti. Tu mogu da spadaju nalozi koje su otvorile aplikacije koje ste instalirali. Ova dozvola omogućava aplikacijama da čuvaju podatke o kontaktima i zlonamerne aplikacije mogu da dele podatke o kontaktima bez vašeg znanja."</string>
- <string name="permlab_writeContacts" msgid="8919430536404830430">"izmena kontakata"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Dozvoljava aplikaciji da menja podatke o kontaktima koje čuvate na tabletu. Ova dozvola omogućava aplikacijama da brišu podatke o kontaktima."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Dozvoljava aplikaciji da menja podatke o kontaktima koje čuvate na Android TV uređaju. Ova dozvola omogućava aplikacijama da brišu podatke o kontaktima."</string>
- <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Dozvoljava aplikaciji da menja podatke o kontaktima koje čuvate na telefonu. Ova dozvola omogućava aplikacijama da brišu podatke o kontaktima."</string>
- <string name="permlab_readCallLog" msgid="1739990210293505948">"čitanje evidencije poziva"</string>
- <string name="permdesc_readCallLog" msgid="8964770895425873433">"Ova aplikacija može da čita istoriju poziva."</string>
- <string name="permlab_writeCallLog" msgid="670292975137658895">"pisanje evidencije poziva"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Dozvoljava aplikaciji da menja evidenciju poziva na tabletu, uključujući podatke o dolaznim i odlaznim pozivima. Zlonamerne aplikacije mogu ovo da koriste da bi brisale ili menjale evidenciju poziva."</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Dozvoljava aplikaciji da menja evidenciju poziva na Android TV uređaju, uključujući podatke o dolaznim i odlaznim pozivima. Zlonamerne aplikacije mogu ovo da koriste za brisanje ili menjanje evidencije poziva."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Dozvoljava aplikaciji da menja evidenciju poziva na telefonu, uključujući podatke o dolaznim i odlaznim pozivima. Zlonamerne aplikacije mogu ovo da koriste da bi brisale ili menjale evidenciju poziva."</string>
- <string name="permlab_bodySensors" msgid="662918578601619569">"Pristup podacima senzora za telo, kao što je puls, u toku korišćenja"</string>
- <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Dozvoljava aplikaciji da pristupa podacima senzora za telo, kao što su puls, temperatura i procenat kiseonika u krvi dok se aplikacija koristi."</string>
- <string name="permlab_bodySensors_background" msgid="4912560779957760446">"Pristup podacima senzora za telo, kao što je puls, u pozadini"</string>
- <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Dozvoljava aplikaciji da pristupa podacima senzora za telo, kao što su puls, temperatura i procenat kiseonika u krvi dok je aplikacija u pozadini."</string>
- <string name="permlab_readCalendar" msgid="6408654259475396200">"Čitanje događaja i podataka iz kalendara"</string>
- <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ova aplikacija može da čita sve događaje iz kalendara koje čuvate na tabletu, kao i da deli ili čuva podatke iz kalendara."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ova aplikacija može da čita sve događaje iz kalendara koje čuvate na Android TV uređaju, kao i da deli ili čuva podatke iz kalendara."</string>
- <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"Ova aplikacija može da čita sve događaje iz kalendara koje čuvate na telefonu, kao i da deli ili čuva podatke iz kalendara."</string>
- <string name="permlab_writeCalendar" msgid="6422137308329578076">"dodavanje ili izmena kalendarskih događaja i slanje poruka e-pošte gostima bez znanja vlasnika"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"Ova aplikaciji može da dodaje, uklanja ili menja događaje iz kalendara na tabletu. Ova aplikacija može da šalje poruke koje izgledaju kao da ih šalju vlasnici kalendara ili da menja događaje bez znanja vlasnika."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"Ova aplikacija može da dodaje, uklanja ili menja događaje iz kalendara na Android TV uređaju. Ova aplikacija može da šalje poruke koje izgledaju kao da ih šalju vlasnici kalendara ili da menja događaje bez znanja vlasnika."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Ova aplikaciji može da dodaje, uklanja ili menja događaje iz kalendara na telefonu. Ova aplikacija može da šalje poruke koje izgledaju kao da ih šalju vlasnici kalendara ili da menja događaje bez znanja vlasnika."</string>
- <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"pristup dodatnim komandama dobavljača lokacije"</string>
- <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Omogućava aplikaciji da pristupa dodatnim komandama davaoca usluga lokacije. To može da omogući aplikaciji da utiče na rad GPS-a ili drugih izvora lokacije."</string>
- <string name="permlab_accessFineLocation" msgid="6426318438195622966">"pristup preciznoj lokaciji samo u prvom planu"</string>
- <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Ova aplikacija može da odredi vašu tačnu lokaciju na osnovu usluga lokacije dok se aplikacija koristi. Usluge lokacije za uređaj moraju da budu uključene da bi aplikacija odredila lokaciju. To može da poveća potrošnju baterije."</string>
- <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"pristup približnoj lokaciji samo u prvom planu"</string>
- <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Ova aplikacija može da odredi vašu približnu lokaciju na osnovu usluga lokacije dok se aplikacija koristi. Usluge lokacije za uređaj moraju da budu uključene da bi aplikacija odredila lokaciju."</string>
- <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"pristup lokaciji u pozadini"</string>
- <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Ova aplikacija može da pristupa lokaciji u bilo kom trenutku, čak i dok se aplikacija ne koristi."</string>
- <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"promena audio podešavanja"</string>
- <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Dozvoljava aplikaciji da menja globalna audio podešavanja kao što su jačina zvuka i izbor zvučnika koji se koristi kao izlaz."</string>
- <string name="permlab_recordAudio" msgid="1208457423054219147">"snimanje audio zapisa"</string>
- <string name="permdesc_recordAudio" msgid="5857246765327514062">"Ova aplikacija može da snima zvuk pomoću mikrofona dok se aplikacija koristi."</string>
- <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"da snima zvuk u pozadini"</string>
- <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ova aplikacija može da snima zvuk pomoću mikrofona u bilo kom trenutku."</string>
- <string name="permlab_detectScreenCapture" msgid="4447042362828799433">"otkrivanje snimanja ekrana u prozorima aplikacija"</string>
- <string name="permdesc_detectScreenCapture" msgid="3485784917960342284">"Ako se tokom korišćenja ove aplikacije napravi snimak ekrana, aplikacija će dobiti obaveštenje."</string>
- <string name="permlab_sim_communication" msgid="176788115994050692">"slanje komandi na SIM"</string>
- <string name="permdesc_sim_communication" msgid="4179799296415957960">"Omogućava aplikaciji da šalje komande SIM kartici. To je veoma opasno."</string>
- <string name="permlab_activityRecognition" msgid="1782303296053990884">"prepoznavanje fizičkih aktivnosti"</string>
- <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ova aplikacija može da prepozna fizičke aktivnosti."</string>
- <string name="permlab_camera" msgid="6320282492904119413">"snimanje fotografija i video snimaka"</string>
- <string name="permdesc_camera" msgid="5240801376168647151">"Ova aplikacija može da snima slike i video snimke pomoću kamere dok se aplikacija koristi."</string>
- <string name="permlab_backgroundCamera" msgid="7549917926079731681">"da snima slike i video snimke u pozadini"</string>
- <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ova aplikacija može da snima fotografije i video snimke pomoću kamere u bilo kom trenutku."</string>
- <string name="permlab_systemCamera" msgid="3642917457796210580">"Dozvolite nekoj aplikaciji ili usluzi da pristupa kamerama sistema da bi snimala slike i video snimke"</string>
- <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ova privilegovana sistemska aplikacija može da snima slike i video snimke pomoću kamere sistema u bilo kom trenutku. Aplikacija treba da ima i dozvolu android.permission.CAMERA"</string>
- <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Dozvolite aplikaciji ili usluzi da dobija povratne pozive o otvaranju ili zatvaranju uređaja sa kamerom."</string>
- <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ova aplikacija može da dobija povratne pozive kada se bilo koji uređaj sa kamerom otvara ili zatvara (pomoću neke aplikacije)."</string>
- <string name="permlab_vibrate" msgid="8596800035791962017">"kontrola vibracije"</string>
- <string name="permdesc_vibrate" msgid="8733343234582083721">"Dozvoljava aplikaciji da kontroliše vibraciju."</string>
- <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Dozvoljava aplikaciji da pristupa stanju vibriranja."</string>
- <string name="permlab_callPhone" msgid="1798582257194643320">"direktno pozivanje brojeva telefona"</string>
- <string name="permdesc_callPhone" msgid="5439809516131609109">"Dozvoljava aplikaciji da poziva brojeve telefona bez vaše dozvole. Ovo može da dovede do neočekivanih troškova ili poziva. Imajte na umu da ovo ne dozvoljava aplikaciji da poziva brojeve za hitne slučajeve. Zlonamerne aplikacije mogu da pozivaju bez vaše potvrde, što može da dovede do troškova."</string>
- <string name="permlab_accessImsCallService" msgid="442192920714863782">"pristup usluzi poziva pomoću razmene trenutnih poruka"</string>
- <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Dozvoljava aplikaciji da koristi uslugu razmene trenutnih poruka da bi upućivala pozive bez vaše intervencije."</string>
- <string name="permlab_readPhoneState" msgid="8138526903259297969">"čitanje statusa i identiteta telefona"</string>
- <string name="permdesc_readPhoneState" msgid="7229063553502788058">"Dozvoljava aplikaciji da pristupa funkcijama telefona na uređaju. Ova dozvola omogućava aplikaciji da utvrdi broj telefona i ID-ove uređaja, zatim da li je poziv aktivan, kao i broj daljinskog uređaja sa kojim je uspostavljen poziv."</string>
- <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"očitavanje osnovnog telefonskog statusa i identiteta"</string>
- <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Omogućava aplikaciji da pristupa osnovnim telefonskim funkcijama uređaja."</string>
- <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"preusmeravanje poziva preko sistema"</string>
- <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Dozvoljava aplikaciji da preusmerava pozive preko sistema da bi poboljšala doživljaj pozivanja."</string>
- <string name="permlab_callCompanionApp" msgid="3654373653014126884">"pregled i kontrola poziva preko sistema."</string>
- <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Dozvoljava aplikaciji da pregleda i kontroliše trenutne pozive na uređaju. To obuhvata informacije poput brojeva telefona i statusa poziva."</string>
- <string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"izuzimanje iz ograničenja za snimanje zvuka"</string>
- <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"Izuzmite aplikaciju iz ograničenja za snimanje zvuka."</string>
- <string name="permlab_acceptHandover" msgid="2925523073573116523">"nastavi poziv u drugoj aplikaciji"</string>
- <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"Dozvoljava aplikaciji da nastavi poziv koji je započet u drugoj aplikaciji."</string>
- <string name="permlab_readPhoneNumbers" msgid="5668704794723365628">"čitanje brojeva telefona"</string>
- <string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Dozvoljava aplikaciji da pristupa brojevima telefona na uređaju."</string>
- <string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"ne isključuj ekran u automobilu"</string>
- <string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"sprečavanje prelaska tableta u stanje spavanja"</string>
- <string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"sprečava Android TV uređaj da pređe u stanje spavanja"</string>
- <string name="permlab_wakeLock" product="default" msgid="569409726861695115">"sprečavanje prelaska telefona u stanje spavanja"</string>
- <string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"Dozvoljava aplikaciji da ne isključuje ekran u automobilu."</string>
- <string name="permdesc_wakeLock" product="tablet" msgid="2441742939101526277">"Dozvoljava aplikaciji da spreči tablet da pređe u stanje spavanja."</string>
- <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"Dozvoljava aplikaciji da spreči Android TV uređaj da pređe u stanje spavanja."</string>
- <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"Dozvoljava aplikaciji da spreči telefon da pređe u stanje spavanja."</string>
- <string name="permlab_transmitIr" msgid="8077196086358004010">"prenos infracrvenih zraka"</string>
- <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Dozvoljava aplikaciji da koristi odašiljač infracrvenih zraka tableta."</string>
- <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Dozvoljava da aplikacija koristi odašiljač infracrvenih zraka na Android TV uređaju."</string>
- <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Dozvoljava aplikaciji da koristi odašiljač infracrvenih zraka telefona."</string>
- <string name="permlab_setWallpaper" msgid="6959514622698794511">"podešavanje pozadine"</string>
- <string name="permdesc_setWallpaper" msgid="2973996714129021397">"Dozvoljava aplikaciji da postavlja pozadinu sistema."</string>
- <string name="permlab_setWallpaperHints" msgid="1153485176642032714">"prilagođavanje veličine pozadine"</string>
- <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Dozvoljava aplikaciji da podesi savete za sistemsku veličinu pozadine."</string>
- <string name="permlab_setTimeZone" msgid="7922618798611542432">"podešavanje vremenske zone"</string>
- <string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"Dozvoljava aplikaciji da promeni vremensku zonu tableta."</string>
- <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Dozvoljava aplikaciji da menja vremensku zonu Android TV uređaja."</string>
- <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Dozvoljava aplikaciji da promeni vremensku zonu telefona."</string>
- <string name="permlab_getAccounts" msgid="5304317160463582791">"pronalaženje naloga na uređaju"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Dozvoljava aplikaciji da preuzima listu naloga poznatih tabletu. Ovo može da obuhvata bilo koje naloge koje prave aplikacije koje instalirate."</string>
- <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Dozvoljava aplikaciji da dođe do liste naloga poznatih Android TV uređaju. Ovo može da obuhvata sve naloge koje otvaraju aplikacije koje ste instalirali."</string>
- <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Dozvoljava aplikaciji da preuzima listu naloga poznatih telefonu. Ovo može da obuhvata bilo koje naloge koje prave aplikacije koje instalirate."</string>
- <string name="permlab_accessNetworkState" msgid="2349126720783633918">"pregled mrežnih veza"</string>
- <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Dozvoljava aplikaciji da pregleda informacije o mrežnim vezama kao što su informacije o tome koje mreže postoje i koje mreže su povezane."</string>
- <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"ima pun mrežni pristup"</string>
- <string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Dozvoljava aplikaciji da pravi mrežne priključke i koristi prilagođene mrežne protokole. Pregledač i druge aplikacije omogućavaju slanje podataka na Internet, pa ova dozvola nije potrebna za slanje podataka na Internet."</string>
- <string name="permlab_changeNetworkState" msgid="8945711637530425586">"promena veze sa mrežom"</string>
- <string name="permdesc_changeNetworkState" msgid="649341947816898736">"Dozvoljava aplikaciji da menja status povezivanja sa mrežom."</string>
- <string name="permlab_changeTetherState" msgid="9079611809931863861">"promena povezivanja privezivanjem"</string>
- <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Dozvoljava aplikaciji da menja status veze sa privezanom mrežom."</string>
- <string name="permlab_accessWifiState" msgid="5552488500317911052">"pregled WiFi veza"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Dozvoljava aplikaciji da pregleda informacije o WiFi umrežavanju, kao što su informacije o tome da li je WiFi omogućen i nazivi povezanih WiFi uređaja."</string>
- <string name="permlab_changeWifiState" msgid="7947824109713181554">"povezivanje i prekid veze sa WiFi mrežom"</string>
- <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Dozvoljava aplikaciji da se povezuje sa pristupnim tačkama za WiFi i prekida vezu sa njima, kao i da unosi promene u konfiguraciju uređaja za WiFi mreže."</string>
- <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"omogućavanje prijema višesmernog WiFi saobraćaja"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Dozvoljava aplikaciji da prima pakete koji se šalju na sve uređaje na WiFi mreži pomoću višesmernih adresa, a ne samo na tablet. Koristi više napajanja od režima jednosmernog saobraćaja."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Dozvoljava aplikaciji da prima pakete koji se šalju na sve uređaje na WiFi mreži pomoću višesmernih adresa, a ne samo na Android TV uređaj. Koristi više energije od režima bez višesmernog slanja."</string>
- <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Dozvoljava aplikaciji da prima pakete koji se šalju na sve uređaje na WiFi mreži pomoću višesmernih adresa, a ne samo na telefon. Koristi više napajanja od režima jednosmernog saobraćaja."</string>
- <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"pristup Bluetooth podešavanjima"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Dozvoljava aplikaciji da konfiguriše lokalni Bluetooth tablet, kao i da otkrije daljinske uređaje i upari se sa njima."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Dozvoljava aplikaciji da konfiguriše Bluetooth na Android TV uređaju i da otkrije udaljene uređaje i upari se sa njima."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Dozvoljava aplikaciji da konfiguriše lokalni Bluetooth telefon, kao i da otkrije daljinske uređaje i upari se sa njima."</string>
- <string name="permlab_accessWimaxState" msgid="7029563339012437434">"povezivanje i prekid veze sa WiMAX-om"</string>
- <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Dozvoljava aplikaciji da utvrdi da li je WiMAX omogućen, kao i informacije o bilo kojim povezanim WiMAX mrežama."</string>
- <string name="permlab_changeWimaxState" msgid="6223305780806267462">"promeni WiMAX statusa"</string>
- <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Dozvoljava aplikaciji da povezuje tablet sa WiMAX mrežama i prekida veze sa njima."</string>
- <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Dozvoljava aplikaciji da povezuje Android TV uređaj sa WiMAX mrežama i da prekida tu vezu."</string>
- <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Dozvoljava aplikaciji da povezuje telefon sa WiMAX mrežama i prekida veze sa njima."</string>
- <string name="permlab_bluetooth" msgid="586333280736937209">"uparivanje sa Bluetooth uređajima"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Dozvoljava aplikaciji da pregleda konfiguraciju Bluetooth-a na tabletu, kao i da uspostavlja i prihvata veze sa uparenim uređajima."</string>
- <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Dozvoljava aplikaciji da pregleda konfiguraciju Bluetooth-a na Android TV uređaju i da uspostavlja i prihvata veze sa uparenim uređajima."</string>
- <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Dozvoljava aplikaciji da pregleda konfiguraciju Bluetooth-a na telefonu, kao i da uspostavlja i prihvata veze sa uparenim uređajima."</string>
- <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"otkrivanje i uparivanje sa obližnjim Bluetooth uređ."</string>
- <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Dozvoljava aplikaciji da otkriva Bluetooth uređaje u blizini i uparuje se sa njima"</string>
- <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"povezivanje sa uparenim Bluetooth uređajima"</string>
- <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Dozvoljava aplikaciji da se povezuje sa uparenim Bluetooth uređajima"</string>
- <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"oglašavanje na Bluetooth uređajima u blizini"</string>
- <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Dozvoljava aplikaciji da se oglašava na Bluetooth uređajima u blizini"</string>
- <string name="permlab_uwb_ranging" msgid="8141915781475770665">"određivanje razdaljine između uređaja ultra-širokog pojasa u blizini"</string>
- <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dozvoljava aplikaciji da određuje relativnu razdaljinu između uređaja ultra-širokog pojasa u blizini"</string>
- <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcija sa WiFi uređajima u blizini"</string>
- <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Dozvoljava aplikaciji da se oglašava, povezuje i utvrđuje relativnu poziciju WiFi uređaja u blizini"</string>
- <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o željenoj NFC usluzi za plaćanje"</string>
- <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Dozvoljava aplikaciji da preuzima informacije o željenoj NFC usluzi za plaćanje, poput registrovanih identifikatora aplikacija i odredišta preusmeravanja."</string>
- <string name="permlab_nfc" msgid="1904455246837674977">"kontrola komunikacije u užem polju (Near Field Communication)"</string>
- <string name="permdesc_nfc" msgid="8352737680695296741">"Dozvoljava aplikaciji da komunicira sa oznakama, karticama i čitačima komunikacije kratkog dometa (NFC)."</string>
- <string name="permlab_disableKeyguard" msgid="3605253559020928505">"onemogućavanje zaključavanja ekrana"</string>
- <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Dozvoljava aplikaciji da onemogući zaključavanje tastature i sve povezane bezbednosne mere sa lozinkama. Na primer, telefon onemogućava zaključavanje tastature pri prijemu dolaznog telefonskog poziva, a zatim ga ponovo omogućava po završetku poziva."</string>
- <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"traženje složenosti zaključavanja ekrana"</string>
- <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Dozvoljava aplikaciji da sazna nivo složenosti zaključavanja ekrana (visoka, srednja, niska ili nijedna), što ukazuje na mogući opseg trajanja i tip zaključavanja ekrana. Aplikacija može i da predlaže korisnicima da ažuriraju zaključavanje ekrana na određeni nivo, ali korisnici slobodno mogu da zanemare to i da idu na druge stranice. Imajte na umu da se podaci za zaključavanje ekrana ne čuvaju kao običan tekst, pa aplikacija ne zna tačnu lozinku."</string>
- <string name="permlab_postNotification" msgid="4875401198597803658">"prikazivanje obaveštenja"</string>
- <string name="permdesc_postNotification" msgid="5974977162462877075">"Dozvoljava aplikaciji da prikazuje obaveštenja"</string>
- <string name="permlab_turnScreenOn" msgid="219344053664171492">"uključivanje ekrana"</string>
- <string name="permdesc_turnScreenOn" msgid="4394606875897601559">"Dozvoljava aplikaciji da uključi ekran."</string>
- <string name="permlab_useBiometric" msgid="6314741124749633786">"koristi biometrijski hardver"</string>
- <string name="permdesc_useBiometric" msgid="7502858732677143410">"Dozvoljava aplikaciji da koristi biometrijski hardver za potvrdu identiteta"</string>
- <string name="permlab_manageFingerprint" msgid="7432667156322821178">"upravljaj hardverom za otiske prstiju"</string>
- <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Dozvoljava aplikaciji da aktivira metode za dodavanje i brisanje šablona otisaka prstiju koji će se koristiti."</string>
- <string name="permlab_useFingerprint" msgid="1001421069766751922">"koristi hardver za otiske prstiju"</string>
- <string name="permdesc_useFingerprint" msgid="412463055059323742">"Dozvoljava aplikaciji da koristi hardver za otiske prstiju radi potvrde identiteta"</string>
- <string name="permlab_audioWrite" msgid="8501705294265669405">"izmena muzičke kolekcije"</string>
- <string name="permdesc_audioWrite" msgid="8057399517013412431">"Dozvoljava aplikaciji da menja muzičku kolekciju."</string>
- <string name="permlab_videoWrite" msgid="5940738769586451318">"izmena video kolekcije"</string>
- <string name="permdesc_videoWrite" msgid="6124731210613317051">"Dozvoljava aplikaciji da menja video kolekciju."</string>
- <string name="permlab_imagesWrite" msgid="1774555086984985578">"izmena kolekcije slika"</string>
- <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Dozvoljava aplikaciji da menja kolekciju slika."</string>
- <string name="permlab_mediaLocation" msgid="7368098373378598066">"čitanje lokacija iz medijske kolekcije"</string>
- <string name="permdesc_mediaLocation" msgid="597912899423578138">"Dozvoljava aplikaciji da čita lokacije iz medijske kolekcije."</string>
- <string name="biometric_app_setting_name" msgid="3339209978734534457">"Koristite biometriju"</string>
- <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Koristite biometriju ili zaključavanje ekrana"</string>
- <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrdite svoj identitet"</string>
- <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Koristite biometrijski podatak da biste nastavili"</string>
- <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Koristite biometrijski podatak ili zaključavanje ekrana da biste nastavili"</string>
- <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string>
- <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Potvrda identiteta je otkazana"</string>
- <string name="biometric_not_recognized" msgid="5106687642694635888">"Nije prepoznato"</string>
- <string name="biometric_error_canceled" msgid="8266582404844179778">"Potvrda identiteta je otkazana"</string>
- <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Niste podesili ni PIN, ni šablon, ni lozinku"</string>
- <string name="biometric_error_generic" msgid="6784371929985434439">"Greška pri potvrdi identiteta"</string>
- <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="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 name="permgroupdesc_sms" msgid="5726462398070064542">"шаље и прегледа SMS поруке"</string>
+ <string name="permgrouplab_storage" msgid="17339216290379241">"Фајлови"</string>
+ <string name="permgroupdesc_storage" msgid="5378659041354582769">"приступ фајловима на уређају"</string>
+ <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"Музика и звук"</string>
+ <string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"приступ музици и аудио садржају на уређају"</string>
+ <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"Слике и видео снимци"</string>
+ <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"приступ сликама и видео снимцима на уређају"</string>
+ <string name="permgrouplab_microphone" msgid="2480597427667420076">"Микрофон"</string>
+ <string name="permgroupdesc_microphone" msgid="1047786732792487722">"снима звук"</string>
+ <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Физичке активности"</string>
+ <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"приступ физичким активностима"</string>
+ <string name="permgrouplab_camera" msgid="9090413408963547706">"Камера"</string>
+ <string name="permgroupdesc_camera" msgid="7585150538459320326">"снима слике и видео"</string>
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Уређаји у близини"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"откривање уређаја у близини и повезивање са њима"</string>
+ <string name="permgrouplab_calllog" msgid="7926834372073550288">"Евиденције позива"</string>
+ <string name="permgroupdesc_calllog" msgid="2026996642917801803">"читање и писање евиденције позива на телефону"</string>
+ <string name="permgrouplab_phone" msgid="570318944091926620">"Телефон"</string>
+ <string name="permgroupdesc_phone" msgid="270048070781478204">"упућује телефонске позиве и управља њима"</string>
+ <string name="permgrouplab_sensors" msgid="9134046949784064495">"Сензори за тело"</string>
+ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"приступа подацима сензора о виталним функцијама"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Обавештења"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"приказивање обавештења"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"да преузима садржај прозора"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Проверава садржај прозора са којим остварујете интеракцију."</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"да укључи Истраживања додиром"</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Ставке које додирнете ће бити изговорене наглас, а можете да се крећете по екрану покретима."</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"да прати текст који уносите"</string>
+ <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Обухвата личне податке као што су бројеви кредитних картица и лозинке."</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"да управља увећањем приказа"</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Управља нивоом зумирања приказа и одређивањем положаја."</string>
+ <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Обављање покрета"</string>
+ <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Може да додирује, листа, скупља приказ и обавља друге покрете."</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Покрети за отисак прста"</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Може да региструје покрете на сензору за отисак прста на уређају."</string>
+ <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Направи снимак екрана"</string>
+ <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Може да направи снимак екрана."</string>
+ <string name="permlab_statusBar" msgid="8798267849526214017">"онемогућавање или измена статусне траке"</string>
+ <string name="permdesc_statusBar" msgid="5809162768651019642">"Дозвољава апликацији да онемогући статусну траку или да додаје и уклања системске иконе."</string>
+ <string name="permlab_statusBarService" msgid="2523421018081437981">"функционисање као статусна трака"</string>
+ <string name="permdesc_statusBarService" msgid="6652917399085712557">"Дозвољава апликацији да функционише као статусна трака."</string>
+ <string name="permlab_expandStatusBar" msgid="1184232794782141698">"проширење/скупљање статусне траке"</string>
+ <string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Дозвољава апликацији да проширује или скупља статусну траку."</string>
+ <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"приказује обавештења као активности преко целог екрана на закључаном уређају"</string>
+ <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Омогућава апликацији да на закључаном уређају приказује обавештења као активности преко целог екрана."</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Инсталирање пречица"</string>
+ <string name="permdesc_install_shortcut" msgid="4476328467240212503">"да додају пречице на почетни екран без интервенције корисника."</string>
+ <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"деинсталирање пречица"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Омогућава апликацији да уклања пречице са почетног екрана без интервенције корисника."</string>
+ <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"преусмеравање одлазних позива"</string>
+ <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Дозвољава апликацији да види који број се бира при одлазном позиву уз опцију да преусмери позив на други број или га потпуно прекине."</string>
+ <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"одговарај на телефонске позиве"</string>
+ <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Дозвољава апликацији да одговори на долазни телефонски позив."</string>
+ <string name="permlab_receiveSms" msgid="505961632050451881">"пријем текстуалних порука (SMS)"</string>
+ <string name="permdesc_receiveSms" msgid="1797345626687832285">"Дозвољава апликацији да прима и обрађује SMS поруке. То значи да апликација може да надгледа или брише поруке које се шаљу уређају, а да вам их не прикаже."</string>
+ <string name="permlab_receiveMms" msgid="4000650116674380275">"пријем текстуалних порука (MMS)"</string>
+ <string name="permdesc_receiveMms" msgid="958102423732219710">"Дозвољава апликацији да прима и обрађује MMS поруке. То значи да апликација може да надгледа или брише поруке које се шаљу уређају, а да вам их не прикаже."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Прослеђивање порука за мобилне уређаје на локалитету"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Дозвољава апликацији да се везује за модул порука за мобилне уређаје на локалитету да би прослеђивала поруке за мобилне уређаје на локалитету онако како су примљене. Обавештења порука за мобилне уређаје на локалитету се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на перформансе или ометају рад уређаја када се прими порука о хитном случају за мобилне уређаје на локалитету."</string>
+ <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Управљање одлазним позивима"</string>
+ <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Омогућава апликацији да види детаље о одлазним позивима на уређају и да контролише те позиве."</string>
+ <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"читање порука инфо сервиса"</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Омогућава апликацији да чита поруке инфо сервиса које уређај прима. Упозорења инфо сервиса се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на перформансе или ометају функционисање уређаја када се прими порука инфо сервиса о хитном случају."</string>
+ <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"читање пријављених фидова"</string>
+ <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Дозвољава апликацији да преузима детаље о тренутно синхронизованим фидовима."</string>
+ <string name="permlab_sendSms" msgid="7757368721742014252">"шаље и прегледа SMS поруке"</string>
+ <string name="permdesc_sendSms" msgid="6757089798435130769">"Дозвољава апликацији да шаље SMS поруке. Ово може да доведе до неочекиваних трошкова. Злонамерне апликације могу да шаљу поруке без ваше потврде, што може да изазове трошкове."</string>
+ <string name="permlab_readSms" msgid="5164176626258800297">"читање текстуалних порука (SMS или MMS)"</string>
+ <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Ова апликација може да чита све SMS (текстуалне) поруке које се чувају на таблету."</string>
+ <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Ова апликација може да чита све SMS (текстуалне) поруке које се чувају на Android TV уређају."</string>
+ <string name="permdesc_readSms" product="default" msgid="774753371111699782">"Ова апликација може да чита све SMS (текстуалне) поруке које се чувају на телефону."</string>
+ <string name="permlab_receiveWapPush" msgid="4223747702856929056">"пријем текстуалних порука (WAP)"</string>
+ <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Дозвољава апликацији да прима и обрађује WAP поруке. Ова дозвола укључује могућност праћења или брисања порука које вам се шаљу, а које вам се не приказују."</string>
+ <string name="permlab_getTasks" msgid="7460048811831750262">"преузимање покренутих апликација"</string>
+ <string name="permdesc_getTasks" msgid="7388138607018233726">"Дозвољава апликацији да преузима информације о актуелним и недавно покренутим задацима. Ово може да омогући апликацији да открије информације о томе које се апликације користе на уређају."</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"управљање власницима профила и уређаја"</string>
+ <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"Дозвољава апликацији да подеси власнике профила и власника уређаја."</string>
+ <string name="permlab_reorderTasks" msgid="7598562301992923804">"промена редоследа покренутих апликација"</string>
+ <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Дозвољава апликацији да премешта задатке у први план и у позадину. Апликација може да ради ово без вашег уноса."</string>
+ <string name="permlab_enableCarMode" msgid="893019409519325311">"омогућавање режима рада у аутомобилу"</string>
+ <string name="permdesc_enableCarMode" msgid="56419168820473508">"Дозвољава апликацији да омогући режим рада у аутомобилу."</string>
+ <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"затварање других апликација"</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"Дозвољава апликацији да заустави позадинске процесе других апликација. Ово може да заустави друге апликације."</string>
+ <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"Ова апликација може да се приказује преко других апликација"</string>
+ <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"Ова апликација може да се приказује преко других апликација или других делова делова екрана. То може да омета стандардно коришћење апликација и начин на који се друге апликације приказују."</string>
+ <string name="permlab_runInBackground" msgid="541863968571682785">"покретање у позадини"</string>
+ <string name="permdesc_runInBackground" msgid="4344539472115495141">"Ова апликација може да се покреће у позадини. То може брже да истроши батерију."</string>
+ <string name="permlab_useDataInBackground" msgid="783415807623038947">"коришћење података у позадини"</string>
+ <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"Ова апликација може да користи податке у позадини. То може да повећа потрошњу података."</string>
+ <string name="permlab_schedule_exact_alarm" msgid="6683283918033029730">"Заказивање временски прецизних радњи"</string>
+ <string name="permdesc_schedule_exact_alarm" msgid="8198009212013211497">"Ова апликација може да закаже да се рад догоди у жељено време у будућности. То значи и да апликација може да ради када не користите активно уређај."</string>
+ <string name="permlab_use_exact_alarm" msgid="348045139777131552">"Заказивање аларма или подсетника за догађаје"</string>
+ <string name="permdesc_use_exact_alarm" msgid="7033761461886938912">"Ова апликација може да заказује радње попут аларма и подсетника да би вас обавештавала у жељено време у будућности."</string>
+ <string name="permlab_persistentActivity" msgid="464970041740567970">"омогућавање непрекидне активности апликације"</string>
+ <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Дозвољава апликацији да учини сопствене компоненте трајним у меморији. Ово може да ограничи меморију доступну другим апликацијама и успори таблет."</string>
+ <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Дозвољава апликацији да трајно задржи неке своје делове у меморији. Ово може да ограничи меморију доступну другим апликацијама и успори Android TV уређај."</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Дозвољава апликацији да учини сопствене компоненте трајним у меморији. Ово може да ограничи меморију доступну другим апликацијама и успори телефон."</string>
+ <string name="permlab_foregroundService" msgid="1768855976818467491">"покрени услугу у првом плану"</string>
+ <string name="permdesc_foregroundService" msgid="8720071450020922795">"Дозвољава апликацији да користи услуге у првом плану."</string>
+ <string name="permlab_foregroundServiceCamera" msgid="7814751737955715297">"покретање услуге у првом плану која припада типу „camera“"</string>
+ <string name="permdesc_foregroundServiceCamera" msgid="6973701931250595727">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „camera“"</string>
+ <string name="permlab_foregroundServiceConnectedDevice" msgid="3019650546176872501">"покретање услуге у првом плану која припада типу „connectedDevice“"</string>
+ <string name="permdesc_foregroundServiceConnectedDevice" msgid="1067457315741352963">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „connectedDevice“"</string>
+ <string name="permlab_foregroundServiceDataSync" msgid="5847463514326881076">"покретање услуге у првом плану која припада типу „dataSync“"</string>
+ <string name="permdesc_foregroundServiceDataSync" msgid="2267140263423973050">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „dataSync“"</string>
+ <string name="permlab_foregroundServiceLocation" msgid="3745428302378535690">"покретање услуге у првом плану која припада типу „location“"</string>
+ <string name="permdesc_foregroundServiceLocation" msgid="118894034365177183">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „location“"</string>
+ <string name="permlab_foregroundServiceMediaPlayback" msgid="4002687983891935514">"покретање услуге у првом плану која припада типу „mediaPlayback“"</string>
+ <string name="permdesc_foregroundServiceMediaPlayback" msgid="3638032446063968043">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „mediaPlayback“"</string>
+ <string name="permlab_foregroundServiceMediaProjection" msgid="2630868915733312527">"покретање услуге у првом плану која припада типу „mediaProjection“"</string>
+ <string name="permdesc_foregroundServiceMediaProjection" msgid="4805677128082002298">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „mediaProjection“"</string>
+ <string name="permlab_foregroundServiceMicrophone" msgid="7390033424890545399">"покретање услуге у првом плану која припада типу „microphone“"</string>
+ <string name="permdesc_foregroundServiceMicrophone" msgid="1206041516173483201">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „microphone“"</string>
+ <string name="permlab_foregroundServicePhoneCall" msgid="627937743867697892">"покретање услуге у првом плану која припада типу „phoneCall“"</string>
+ <string name="permdesc_foregroundServicePhoneCall" msgid="5941660252587015147">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „phoneCall“"</string>
+ <string name="permlab_foregroundServiceHealth" msgid="3675776442080928184">"покретање услуге у првом плану која припада типу „health“"</string>
+ <string name="permdesc_foregroundServiceHealth" msgid="2024586220562667185">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „health“"</string>
+ <string name="permlab_foregroundServiceRemoteMessaging" msgid="105670277002780950">"покретање услуге у првом плану која припада типу „remoteMessaging“"</string>
+ <string name="permdesc_foregroundServiceRemoteMessaging" msgid="8767598075877576277">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „remoteMessaging“"</string>
+ <string name="permlab_foregroundServiceSystemExempted" msgid="1597663713590612685">"покретање услуге у првом плану која припада типу „systemExempted“"</string>
+ <string name="permdesc_foregroundServiceSystemExempted" msgid="947381760834649622">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „systemExempted“"</string>
+ <string name="permlab_foregroundServiceFileManagement" msgid="2585000987966045030">"покретање услуге у првом плану која припада типу „fileManagement“"</string>
+ <string name="permdesc_foregroundServiceFileManagement" msgid="417103601269698508">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „fileManagement“"</string>
+ <string name="permlab_foregroundServiceSpecialUse" msgid="7973536745876645082">"покретање услуге у првом плану која припада типу „specialUse“"</string>
+ <string name="permdesc_foregroundServiceSpecialUse" msgid="646713654541885919">"Дозвољава апликацији да користи услуге у првом плану које припадају типу „specialUse“"</string>
+ <string name="permlab_getPackageSize" msgid="375391550792886641">"мерење меморијског простора у апликацији"</string>
+ <string name="permdesc_getPackageSize" msgid="742743530909966782">"Дозвољава апликацији да преузме величине кôда, података и кеша."</string>
+ <string name="permlab_writeSettings" msgid="8057285063719277394">"измена подешавања система"</string>
+ <string name="permdesc_writeSettings" msgid="8293047411196067188">"Дозвољава апликацији да мења податке о подешавању система. Злонамерне апликације могу да оштете конфигурацију система."</string>
+ <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"покретање при покретању система"</string>
+ <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Омогућава да се апликација покрене одмах након покретања система. То може да успори покретање таблета, при чему ова апликација може да успори функционисање целог таблета тиме што ће увек бити активна."</string>
+ <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Дозвољава апликацији да се покрене одмах по укључивању система. То може да успори покретање Android TV уређаја и апликација може да успори функционисање уређаја у целини тако што ће увек бити активна."</string>
+ <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Омогућава да се апликација покрене чим се систем покрене. То може да успори покретање телефона, при чему ова апликација може да успори функционисање целог телефона тиме што ће увек бити активна."</string>
+ <string name="permlab_broadcastSticky" msgid="4552241916400572230">"слање пријемчивих емитовања"</string>
+ <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Дозвољава апликацији да шаље пријемчива емитовања, која остају по завршетку емитовања. Прекомерна употреба може да успори или дестабилизује таблет тако што ће га приморати да троши превише меморије."</string>
+ <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Дозвољава апликацији да шаље лепљива емитовања која остају по завршетку емитовања. Прекомерна употреба може да успори или дестабилизује Android TV уређај тако што ће га приморати да троши превише меморије."</string>
+ <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Дозвољава апликацији да шаље пријемчива емитовања, која остају по завршетку емитовања. Прекомерна употреба може да успори или дестабилизује телефон тако што ће га приморати да троши превише меморије."</string>
+ <string name="permlab_readContacts" msgid="8776395111787429099">"читање контаката"</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Дозвољава апликацији да чита податке о контактима које чувате на таблету. Апликације ће имати приступ и налозима на вашем таблету на којима су направљени контакти. Ту могу да спадају налози које су отвориле апликације које сте инсталирали. Ова дозвола омогућава апликацијама да чувају податке о контактима и злонамерне апликације могу да деле податке о контактима без вашег знања."</string>
+ <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Дозвољава апликацији да чита податке о контактима које чувате на Android TV уређају. Апликације ће имати приступ и налозима на вашем Android TV уређају на којима су направљени контакти. Ту могу да спадају налози које су отвориле апликације које сте инсталирали. Ова дозвола омогућава апликацијама да чувају податке о контактима и злонамерне апликације могу да деле податке о контактима без вашег знања."</string>
+ <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Дозвољава апликацији да чита податке о контактима које чувате на телефону. Апликације ће имати приступ и налозима на вашем телефону на којима су направљени контакти. Ту могу да спадају налози које су отвориле апликације које сте инсталирали. Ова дозвола омогућава апликацијама да чувају податке о контактима и злонамерне апликације могу да деле податке о контактима без вашег знања."</string>
+ <string name="permlab_writeContacts" msgid="8919430536404830430">"измена контаката"</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Дозвољава апликацији да мења податке о контактима које чувате на таблету. Ова дозвола омогућава апликацијама да бришу податке о контактима."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Дозвољава апликацији да мења податке о контактима које чувате на Android TV уређају. Ова дозвола омогућава апликацијама да бришу податке о контактима."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Дозвољава апликацији да мења податке о контактима које чувате на телефону. Ова дозвола омогућава апликацијама да бришу податке о контактима."</string>
+ <string name="permlab_readCallLog" msgid="1739990210293505948">"читање евиденције позива"</string>
+ <string name="permdesc_readCallLog" msgid="8964770895425873433">"Ова апликација може да чита историју позива."</string>
+ <string name="permlab_writeCallLog" msgid="670292975137658895">"писање евиденције позива"</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Дозвољава апликацији да мења евиденцију позива на таблету, укључујући податке о долазним и одлазним позивима. Злонамерне апликације могу ово да користе да би брисале или мењале евиденцију позива."</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Дозвољава апликацији да мења евиденцију позива на Android TV уређају, укључујући податке о долазним и одлазним позивима. Злонамерне апликације могу ово да користе за брисање или мењање евиденције позива."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Дозвољава апликацији да мења евиденцију позива на телефону, укључујући податке о долазним и одлазним позивима. Злонамерне апликације могу ово да користе да би брисале или мењале евиденцију позива."</string>
+ <string name="permlab_bodySensors" msgid="662918578601619569">"Приступ подацима сензора за тело, као што је пулс, у току коришћења"</string>
+ <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Дозвољава апликацији да приступа подацима сензора за тело, као што су пулс, температура и проценат кисеоника у крви док се апликација користи."</string>
+ <string name="permlab_bodySensors_background" msgid="4912560779957760446">"Приступ подацима сензора за тело, као што је пулс, у позадини"</string>
+ <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Дозвољава апликацији да приступа подацима сензора за тело, као што су пулс, температура и проценат кисеоника у крви док је апликација у позадини."</string>
+ <string name="permlab_readCalendar" msgid="6408654259475396200">"Читање догађаја и података из календара"</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ова апликација може да чита све догађаје из календара које чувате на таблету, као и да дели или чува податке из календара."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ова апликација може да чита све догађаје из календара које чувате на Android TV уређају, као и да дели или чува податке из календара."</string>
+ <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"Ова апликација може да чита све догађаје из календара које чувате на телефону, као и да дели или чува податке из календара."</string>
+ <string name="permlab_writeCalendar" msgid="6422137308329578076">"додавање или измена календарских догађаја и слање порука е-поште гостима без знања власника"</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"Ова апликацији може да додаје, уклања или мења догађаје из календара на таблету. Ова апликација може да шаље поруке које изгледају као да их шаљу власници календара или да мења догађаје без знања власника."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"Ова апликација може да додаје, уклања или мења догађаје из календара на Android TV уређају. Ова апликација може да шаље поруке које изгледају као да их шаљу власници календара или да мења догађаје без знања власника."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Ова апликацији може да додаје, уклања или мења догађаје из календара на телефону. Ова апликација може да шаље поруке које изгледају као да их шаљу власници календара или да мења догађаје без знања власника."</string>
+ <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"приступ додатним командама добављача локације"</string>
+ <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Омогућава апликацији да приступа додатним командама даваоца услуга локације. То може да омогући апликацији да утиче на рад GPS-а или других извора локације."</string>
+ <string name="permlab_accessFineLocation" msgid="6426318438195622966">"приступ прецизној локацији само у првом плану"</string>
+ <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Ова апликација може да одреди вашу тачну локацију на основу услуга локације док се апликација користи. Услуге локације за уређај морају да буду укључене да би апликација одредила локацију. То може да повећа потрошњу батерије."</string>
+ <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"приступ приближној локацији само у првом плану"</string>
+ <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Ова апликација може да одреди вашу приближну локацију на основу услуга локације док се апликација користи. Услуге локације за уређај морају да буду укључене да би апликација одредила локацију."</string>
+ <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"приступ локацији у позадини"</string>
+ <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Ова апликација може да приступа локацији у било ком тренутку, чак и док се апликација не користи."</string>
+ <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"промена аудио подешавања"</string>
+ <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Дозвољава апликацији да мења глобална аудио подешавања као што су јачина звука и избор звучника који се користи као излаз."</string>
+ <string name="permlab_recordAudio" msgid="1208457423054219147">"снимање аудио записа"</string>
+ <string name="permdesc_recordAudio" msgid="5857246765327514062">"Ова апликација може да снима звук помоћу микрофона док се апликација користи."</string>
+ <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"да снима звук у позадини"</string>
+ <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ова апликација може да снима звук помоћу микрофона у било ком тренутку."</string>
+ <string name="permlab_detectScreenCapture" msgid="4447042362828799433">"откривање снимања екрана у прозорима апликација"</string>
+ <string name="permdesc_detectScreenCapture" msgid="3485784917960342284">"Ако се током коришћења ове апликације направи снимак екрана, апликација ће добити обавештење."</string>
+ <string name="permlab_sim_communication" msgid="176788115994050692">"слање команди на SIM"</string>
+ <string name="permdesc_sim_communication" msgid="4179799296415957960">"Омогућава апликацији да шаље команде SIM картици. То је веома опасно."</string>
+ <string name="permlab_activityRecognition" msgid="1782303296053990884">"препознавање физичких активности"</string>
+ <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ова апликација може да препозна физичке активности."</string>
+ <string name="permlab_camera" msgid="6320282492904119413">"снимање фотографија и видео снимака"</string>
+ <string name="permdesc_camera" msgid="5240801376168647151">"Ова апликација може да снима слике и видео снимке помоћу камере док се апликација користи."</string>
+ <string name="permlab_backgroundCamera" msgid="7549917926079731681">"да снима слике и видео снимке у позадини"</string>
+ <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ова апликација може да снима фотографије и видео снимке помоћу камере у било ком тренутку."</string>
+ <string name="permlab_systemCamera" msgid="3642917457796210580">"Дозволите некој апликацији или услузи да приступа камерама система да би снимала слике и видео снимке"</string>
+ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ова привилегована системска апликација може да снима слике и видео снимке помоћу камере система у било ком тренутку. Апликација треба да има и дозволу android.permission.CAMERA"</string>
+ <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Дозволите апликацији или услузи да добија повратне позиве о отварању или затварању уређаја са камером."</string>
+ <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ова апликација може да добија повратне позиве када се било који уређај са камером отвара или затвара (помоћу неке апликације)."</string>
+ <string name="permlab_vibrate" msgid="8596800035791962017">"контрола вибрације"</string>
+ <string name="permdesc_vibrate" msgid="8733343234582083721">"Дозвољава апликацији да контролише вибрацију."</string>
+ <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Дозвољава апликацији да приступа стању вибрирања."</string>
+ <string name="permlab_callPhone" msgid="1798582257194643320">"директно позивање бројева телефона"</string>
+ <string name="permdesc_callPhone" msgid="5439809516131609109">"Дозвољава апликацији да позива бројеве телефона без ваше дозволе. Ово може да доведе до неочекиваних трошкова или позива. Имајте на уму да ово не дозвољава апликацији да позива бројеве за хитне случајеве. Злонамерне апликације могу да позивају без ваше потврде, што може да доведе до трошкова."</string>
+ <string name="permlab_accessImsCallService" msgid="442192920714863782">"приступ услузи позива помоћу размене тренутних порука"</string>
+ <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Дозвољава апликацији да користи услугу размене тренутних порука да би упућивала позиве без ваше интервенције."</string>
+ <string name="permlab_readPhoneState" msgid="8138526903259297969">"читање статуса и идентитета телефона"</string>
+ <string name="permdesc_readPhoneState" msgid="7229063553502788058">"Дозвољава апликацији да приступа функцијама телефона на уређају. Ова дозвола омогућава апликацији да утврди број телефона и ИД-ове уређаја, затим да ли је позив активан, као и број даљинског уређаја са којим је успостављен позив."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"очитавање основног телефонског статуса и идентитета"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Омогућава апликацији да приступа основним телефонским функцијама уређаја."</string>
+ <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"преусмеравање позива преко система"</string>
+ <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дозвољава апликацији да преусмерава позиве преко система да би побољшала доживљај позивања."</string>
+ <string name="permlab_callCompanionApp" msgid="3654373653014126884">"преглед и контрола позива преко система."</string>
+ <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Дозвољава апликацији да прегледа и контролише тренутне позиве на уређају. То обухвата информације попут бројева телефона и статуса позива."</string>
+ <string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"изузимање из ограничења за снимање звука"</string>
+ <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"Изузмите апликацију из ограничења за снимање звука."</string>
+ <string name="permlab_acceptHandover" msgid="2925523073573116523">"настави позив у другој апликацији"</string>
+ <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"Дозвољава апликацији да настави позив који је започет у другој апликацији."</string>
+ <string name="permlab_readPhoneNumbers" msgid="5668704794723365628">"читање бројева телефона"</string>
+ <string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Дозвољава апликацији да приступа бројевима телефона на уређају."</string>
+ <string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"не искључуј екран у аутомобилу"</string>
+ <string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"спречавање преласка таблета у стање спавања"</string>
+ <string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"спречава Android TV уређај да пређе у стање спавања"</string>
+ <string name="permlab_wakeLock" product="default" msgid="569409726861695115">"спречавање преласка телефона у стање спавања"</string>
+ <string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"Дозвољава апликацији да не искључује екран у аутомобилу."</string>
+ <string name="permdesc_wakeLock" product="tablet" msgid="2441742939101526277">"Дозвољава апликацији да спречи таблет да пређе у стање спавања."</string>
+ <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"Дозвољава апликацији да спречи Android TV уређај да пређе у стање спавања."</string>
+ <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"Дозвољава апликацији да спречи телефон да пређе у стање спавања."</string>
+ <string name="permlab_transmitIr" msgid="8077196086358004010">"пренос инфрацрвених зрака"</string>
+ <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Дозвољава апликацији да користи одашиљач инфрацрвених зрака таблета."</string>
+ <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Дозвољава да апликација користи одашиљач инфрацрвених зрака на Android TV уређају."</string>
+ <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Дозвољава апликацији да користи одашиљач инфрацрвених зрака телефона."</string>
+ <string name="permlab_setWallpaper" msgid="6959514622698794511">"подешавање позадине"</string>
+ <string name="permdesc_setWallpaper" msgid="2973996714129021397">"Дозвољава апликацији да поставља позадину система."</string>
+ <string name="permlab_setWallpaperHints" msgid="1153485176642032714">"прилагођавање величине позадине"</string>
+ <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Дозвољава апликацији да подеси савете за системску величину позадине."</string>
+ <string name="permlab_setTimeZone" msgid="7922618798611542432">"подешавање временске зоне"</string>
+ <string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"Дозвољава апликацији да промени временску зону таблета."</string>
+ <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Дозвољава апликацији да мења временску зону Android TV уређаја."</string>
+ <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Дозвољава апликацији да промени временску зону телефона."</string>
+ <string name="permlab_getAccounts" msgid="5304317160463582791">"проналажење налога на уређају"</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Дозвољава апликацији да преузима листу налога познатих таблету. Ово може да обухвата било које налоге које праве апликације које инсталирате."</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Дозвољава апликацији да дође до листе налога познатих Android TV уређају. Ово може да обухвата све налоге које отварају апликације које сте инсталирали."</string>
+ <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Дозвољава апликацији да преузима листу налога познатих телефону. Ово може да обухвата било које налоге које праве апликације које инсталирате."</string>
+ <string name="permlab_accessNetworkState" msgid="2349126720783633918">"преглед мрежних веза"</string>
+ <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Дозвољава апликацији да прегледа информације о мрежним везама као што су информације о томе које мреже постоје и које мреже су повезане."</string>
+ <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"има пун мрежни приступ"</string>
+ <string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Дозвољава апликацији да прави мрежне прикључке и користи прилагођене мрежне протоколе. Прегледач и друге апликације омогућавају слање података на Интернет, па ова дозвола није потребна за слање података на Интернет."</string>
+ <string name="permlab_changeNetworkState" msgid="8945711637530425586">"промена везе са мрежом"</string>
+ <string name="permdesc_changeNetworkState" msgid="649341947816898736">"Дозвољава апликацији да мења статус повезивања са мрежом."</string>
+ <string name="permlab_changeTetherState" msgid="9079611809931863861">"промена повезивања привезивањем"</string>
+ <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Дозвољава апликацији да мења статус везе са привезаном мрежом."</string>
+ <string name="permlab_accessWifiState" msgid="5552488500317911052">"преглед WiFi веза"</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Дозвољава апликацији да прегледа информације о WiFi умрежавању, као што су информације о томе да ли је WiFi омогућен и називи повезаних WiFi уређаја."</string>
+ <string name="permlab_changeWifiState" msgid="7947824109713181554">"повезивање и прекид везе са WiFi мрежом"</string>
+ <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Дозвољава апликацији да се повезује са приступним тачкама за WiFi и прекида везу са њима, као и да уноси промене у конфигурацију уређаја за WiFi мреже."</string>
+ <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"омогућавање пријема вишесмерног WiFi саобраћаја"</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Дозвољава апликацији да прима пакете који се шаљу на све уређаје на WiFi мрежи помоћу вишесмерних адреса, а не само на таблет. Користи више напајања од режима једносмерног саобраћаја."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Дозвољава апликацији да прима пакете који се шаљу на све уређаје на WiFi мрежи помоћу вишесмерних адреса, а не само на Android TV уређај. Користи више енергије од режима без вишесмерног слања."</string>
+ <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Дозвољава апликацији да прима пакете који се шаљу на све уређаје на WiFi мрежи помоћу вишесмерних адреса, а не само на телефон. Користи више напајања од режима једносмерног саобраћаја."</string>
+ <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"приступ Bluetooth подешавањима"</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Дозвољава апликацији да конфигурише локални Bluetooth таблет, као и да открије даљинске уређаје и упари се са њима."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Дозвољава апликацији да конфигурише Bluetooth на Android TV уређају и да открије удаљене уређаје и упари се са њима."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Дозвољава апликацији да конфигурише локални Bluetooth телефон, као и да открије даљинске уређаје и упари се са њима."</string>
+ <string name="permlab_accessWimaxState" msgid="7029563339012437434">"повезивање и прекид везе са WiMAX-ом"</string>
+ <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Дозвољава апликацији да утврди да ли је WiMAX омогућен, као и информације о било којим повезаним WiMAX мрежама."</string>
+ <string name="permlab_changeWimaxState" msgid="6223305780806267462">"промени WiMAX статуса"</string>
+ <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Дозвољава апликацији да повезује таблет са WiMAX мрежама и прекида везе са њима."</string>
+ <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Дозвољава апликацији да повезује Android TV уређај са WiMAX мрежама и да прекида ту везу."</string>
+ <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Дозвољава апликацији да повезује телефон са WiMAX мрежама и прекида везе са њима."</string>
+ <string name="permlab_bluetooth" msgid="586333280736937209">"упаривање са Bluetooth уређајима"</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Дозвољава апликацији да прегледа конфигурацију Bluetooth-а на таблету, као и да успоставља и прихвата везе са упареним уређајима."</string>
+ <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Дозвољава апликацији да прегледа конфигурацију Bluetooth-а на Android TV уређају и да успоставља и прихвата везе са упареним уређајима."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Дозвољава апликацији да прегледа конфигурацију Bluetooth-а на телефону, као и да успоставља и прихвата везе са упареним уређајима."</string>
+ <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"откривање и упаривање са оближњим Bluetooth уређ."</string>
+ <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Дозвољава апликацији да открива Bluetooth уређаје у близини и упарује се са њима"</string>
+ <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"повезивање са упареним Bluetooth уређајима"</string>
+ <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Дозвољава апликацији да се повезује са упареним Bluetooth уређајима"</string>
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"оглашавање на Bluetooth уређајима у близини"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Дозвољава апликацији да се оглашава на Bluetooth уређајима у близини"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"одређивање раздаљине између уређаја ултра-широког појаса у близини"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дозвољава апликацији да одређује релативну раздаљину између уређаја ултра-широког појаса у близини"</string>
+ <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"интеракција са WiFi уређајима у близини"</string>
+ <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Дозвољава апликацији да се оглашава, повезује и утврђује релативну позицију WiFi уређаја у близини"</string>
+ <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информације о жељеној NFC услузи за плаћање"</string>
+ <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозвољава апликацији да преузима информације о жељеној NFC услузи за плаћање, попут регистрованих идентификатора апликација и одредишта преусмеравања."</string>
+ <string name="permlab_nfc" msgid="1904455246837674977">"контрола комуникације у ужем пољу (Near Field Communication)"</string>
+ <string name="permdesc_nfc" msgid="8352737680695296741">"Дозвољава апликацији да комуницира са ознакама, картицама и читачима комуникације кратког домета (NFC)."</string>
+ <string name="permlab_disableKeyguard" msgid="3605253559020928505">"онемогућавање закључавања екрана"</string>
+ <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Дозвољава апликацији да онемогући закључавање тастатуре и све повезане безбедносне мере са лозинкама. На пример, телефон онемогућава закључавање тастатуре при пријему долазног телефонског позива, а затим га поново омогућава по завршетку позива."</string>
+ <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"тражење сложености закључавања екрана"</string>
+ <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Дозвољава апликацији да сазна ниво сложености закључавања екрана (висока, средња, ниска или ниједна), што указује на могући опсег трајања и тип закључавања екрана. Апликација може и да предлаже корисницима да ажурирају закључавање екрана на одређени ниво, али корисници слободно могу да занемаре то и да иду на друге странице. Имајте на уму да се подаци за закључавање екрана не чувају као обичан текст, па апликација не зна тачну лозинку."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"приказивање обавештења"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Дозвољава апликацији да приказује обавештења"</string>
+ <string name="permlab_turnScreenOn" msgid="219344053664171492">"укључивање екрана"</string>
+ <string name="permdesc_turnScreenOn" msgid="4394606875897601559">"Дозвољава апликацији да укључи екран."</string>
+ <string name="permlab_useBiometric" msgid="6314741124749633786">"користи биометријски хардвер"</string>
+ <string name="permdesc_useBiometric" msgid="7502858732677143410">"Дозвољава апликацији да користи биометријски хардвер за потврду идентитета"</string>
+ <string name="permlab_manageFingerprint" msgid="7432667156322821178">"управљај хардвером за отиске прстију"</string>
+ <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Дозвољава апликацији да активира методе за додавање и брисање шаблона отисака прстију који ће се користити."</string>
+ <string name="permlab_useFingerprint" msgid="1001421069766751922">"користи хардвер за отиске прстију"</string>
+ <string name="permdesc_useFingerprint" msgid="412463055059323742">"Дозвољава апликацији да користи хардвер за отиске прстију ради потврде идентитета"</string>
+ <string name="permlab_audioWrite" msgid="8501705294265669405">"измена музичке колекције"</string>
+ <string name="permdesc_audioWrite" msgid="8057399517013412431">"Дозвољава апликацији да мења музичку колекцију."</string>
+ <string name="permlab_videoWrite" msgid="5940738769586451318">"измена видео колекције"</string>
+ <string name="permdesc_videoWrite" msgid="6124731210613317051">"Дозвољава апликацији да мења видео колекцију."</string>
+ <string name="permlab_imagesWrite" msgid="1774555086984985578">"измена колекције слика"</string>
+ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Дозвољава апликацији да мења колекцију слика."</string>
+ <string name="permlab_mediaLocation" msgid="7368098373378598066">"читање локација из медијске колекције"</string>
+ <string name="permdesc_mediaLocation" msgid="597912899423578138">"Дозвољава апликацији да чита локације из медијске колекције."</string>
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Користите биометрију"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Користите биометрију или закључавање екрана"</string>
+ <string name="biometric_dialog_default_title" msgid="55026799173208210">"Потврдите свој идентитет"</string>
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Користите биометријски податак да бисте наставили"</string>
+ <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Користите биометријски податак или закључавање екрана да бисте наставили"</string>
+ <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометријски хардвер није доступан"</string>
+ <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Потврда идентитета је отказана"</string>
+ <string name="biometric_not_recognized" msgid="5106687642694635888">"Није препознато"</string>
+ <string name="biometric_error_canceled" msgid="8266582404844179778">"Потврда идентитета је отказана"</string>
+ <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Нисте подесили ни PIN, ни шаблон, ни лозинку"</string>
+ <string name="biometric_error_generic" msgid="6784371929985434439">"Грешка при потврди идентитета"</string>
+ <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="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">
</string-array>
- <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Otisak prsta nije prepoznat"</string>
- <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Otisak prsta nije prepoznat"</string>
- <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string>
- <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je potvrđeno"</string>
- <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="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="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="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>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Ne možete da koristite senzor za otisak prsta. Posetite dobavljača za popravke"</string>
- <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Pritisnuto je dugme za uključivanje"</string>
- <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristite otisak prsta"</string>
- <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Koristite otisak prsta ili zaključavanje ekrana"</string>
- <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string>
- <string name="fingerprint_or_screen_lock_dialog_default_subtitle" msgid="5195808203117992200">"Koristite otisak prsta ili zaključavanje ekrana da biste nastavili"</string>
+ <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отисак прста није препознат"</string>
+ <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Отисак прста није препознат"</string>
+ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отисак прста је потврђен"</string>
+ <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лице је потврђено"</string>
+ <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="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="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>
+ <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Не можете да користите сензор за отисак прста. Посетите добављача за поправке"</string>
+ <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Притиснуто је дугме за укључивање"</string>
+ <string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користите отисак прста"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Користите отисак прста или закључавање екрана"</string>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Наставите помоћу отиска прста"</string>
+ <string name="fingerprint_or_screen_lock_dialog_default_subtitle" msgid="5195808203117992200">"Користите отисак прста или закључавање екрана да бисте наставили"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Došlo je do problema. Probajte ponovo."</string>
- <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otiska prsta"</string>
- <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Otključavanje licem"</string>
- <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problem sa otključavanje licem"</string>
- <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da biste izbrisali model lica, pa ponovo dodajte svoje lice"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Podesite otključavanje licem"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon tako što ćete ga pogledati"</string>
- <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da biste koristili otključavanje licem, uključite "<b>"pristup kameri"</b>" u odeljku Podešavanja &gt; Privatnost"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Podesite još načina za otključavanje"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da biste dodali otisak prsta"</string>
- <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
- <string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Ne možete da koristite senzor za otisak prsta"</string>
- <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>
- <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>
- <string name="face_acquired_too_low" msgid="4075391872960840081">"Pomerite telefon nadole"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"Pomerite telefon ulevo"</string>
- <string name="face_acquired_too_left" msgid="9201762240918405486">"Pomerite telefon udesno"</string>
- <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Gledajte pravo u uređaj."</string>
- <string name="face_acquired_not_detected" msgid="1057966913397548150">"Ne vidi se lice. Držite telefon u visini očiju."</string>
- <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Mnogo se pomerate. Držite telefon mirno."</string>
- <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Ponovo registrujte lice."</string>
- <string name="face_acquired_too_different" msgid="2520389515612972889">"Lice nije prepoznato. Probajte ponovo."</string>
- <string name="face_acquired_too_similar" msgid="8882920552674125694">"Malo pomerite glavu"</string>
- <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Gledajte pravo u telefon"</string>
- <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Gledajte pravo u telefon"</string>
- <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Gledajte pravo u telefon"</string>
- <string name="face_acquired_obscured" msgid="4917643294953326639">"Uklonite sve što vam zaklanja lice."</string>
- <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Očistite gornji deo ekrana, uključujući crnu traku"</string>
+ <string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Дошло је до проблема. Пробајте поново."</string>
+ <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона отиска прста"</string>
+ <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Откључавање лицем"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Проблем са откључавање лицем"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Додирните да бисте избрисали модел лица, па поново додајте своје лице"</string>
+ <string name="face_setup_notification_title" msgid="8843461561970741790">"Подесите откључавање лицем"</string>
+ <string name="face_setup_notification_content" msgid="5463999831057751676">"Откључајте телефон тако што ћете га погледати"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Да бисте користили откључавање лицем, укључите "<b>"приступ камери"</b>" у одељку Подешавања &gt; Приватност"</string>
+ <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Подесите још начина за откључавање"</string>
+ <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Додирните да бисте додали отисак прста"</string>
+ <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Откључавање отиском прста"</string>
+ <string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Не можете да користите сензор за отисак прста"</string>
+ <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>
+ <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>
+ <string name="face_acquired_too_low" msgid="4075391872960840081">"Померите телефон надоле"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"Померите телефон улево"</string>
+ <string name="face_acquired_too_left" msgid="9201762240918405486">"Померите телефон удесно"</string>
+ <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Гледајте право у уређај."</string>
+ <string name="face_acquired_not_detected" msgid="1057966913397548150">"Не види се лице. Држите телефон у висини очију."</string>
+ <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Много се померате. Држите телефон мирно."</string>
+ <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Поново региструјте лице."</string>
+ <string name="face_acquired_too_different" msgid="2520389515612972889">"Лице није препознато. Пробајте поново."</string>
+ <string name="face_acquired_too_similar" msgid="8882920552674125694">"Мало померите главу"</string>
+ <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Гледајте право у телефон"</string>
+ <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Гледајте право у телефон"</string>
+ <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Гледајте право у телефон"</string>
+ <string name="face_acquired_obscured" msgid="4917643294953326639">"Уклоните све што вам заклања лице."</string>
+ <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Очистите горњи део екрана, укључујући црну траку"</string>
<!-- no translation found for face_acquired_dark_glasses_detected (5643703296620631986) -->
<skip />
<!-- no translation found for face_acquired_mouth_covering_detected (8219428572168642593) -->
<skip />
- <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Pravljenje modela lica nije uspelo. Probajte ponovo."</string>
- <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Otkrivene su tamne naočari. Lice mora da bude potpuno vidljivo."</string>
- <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Otkriveno je prekrivanje lica. Lice mora da bude potpuno vidljivo."</string>
+ <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Прављење модела лица није успело. Пробајте поново."</string>
+ <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Откривене су тамне наочари. Лице мора да буде потпуно видљиво."</string>
+ <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Откривено је прекривање лица. Лице мора да буде потпуно видљиво."</string>
<string-array name="face_acquired_vendor">
</string-array>
- <string name="face_error_hw_not_available" msgid="5085202213036026288">"Provera lica nije uspela. Hardver nije dostupan."</string>
- <string name="face_error_timeout" msgid="2598544068593889762">"Probajte ponovo otključavanje licem"</string>
- <string name="face_error_no_space" msgid="5649264057026021723">"Novi podaci o licu nisu sačuvani. Prvo izbrišete prethodne."</string>
- <string name="face_error_canceled" msgid="2164434737103802131">"Obrada lica je otkazana."</string>
- <string name="face_error_user_canceled" msgid="5766472033202928373">"Korisnik je otkazao otključavanje licem"</string>
- <string name="face_error_lockout" msgid="7864408714994529437">"Previše pokušaja. Probajte ponovo kasnije."</string>
- <string name="face_error_lockout_permanent" msgid="3277134834042995260">"Previše pokušaja. Otključavanje licem je onemogućeno."</string>
- <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Previše pokušaja. Koristite zaključavanje ekrana za to."</string>
- <string name="face_error_unable_to_process" msgid="5723292697366130070">"Provera lica nije uspela. Probajte ponovo."</string>
- <string name="face_error_not_enrolled" msgid="1134739108536328412">"Niste podesili otključavanje licem"</string>
- <string name="face_error_hw_not_present" msgid="7940978724978763011">"Otključavanje licem nije podržano na ovom uređaju"</string>
- <string name="face_error_security_update_required" msgid="5076017208528750161">"Senzor je privremeno onemogućen."</string>
- <string name="face_name_template" msgid="3877037340223318119">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="5854024256907828015">"Koristite otključavanje licem"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Koristite zaključavanje licem ili zaključavanje ekrana"</string>
- <string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Potvrdite identitet licem da biste nastavili"</string>
- <string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Koristite lice ili zaključavanje ekrana da biste nastavili"</string>
+ <string name="face_error_hw_not_available" msgid="5085202213036026288">"Провера лица није успела. Хардвер није доступан."</string>
+ <string name="face_error_timeout" msgid="2598544068593889762">"Пробајте поново откључавање лицем"</string>
+ <string name="face_error_no_space" msgid="5649264057026021723">"Нови подаци о лицу нису сачувани. Прво избришете претходне."</string>
+ <string name="face_error_canceled" msgid="2164434737103802131">"Обрада лица је отказана."</string>
+ <string name="face_error_user_canceled" msgid="5766472033202928373">"Корисник је отказао откључавање лицем"</string>
+ <string name="face_error_lockout" msgid="7864408714994529437">"Превише покушаја. Пробајте поново касније."</string>
+ <string name="face_error_lockout_permanent" msgid="3277134834042995260">"Превише покушаја. Откључавање лицем је онемогућено."</string>
+ <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Превише покушаја. Користите закључавање екрана за то."</string>
+ <string name="face_error_unable_to_process" msgid="5723292697366130070">"Провера лица није успела. Пробајте поново."</string>
+ <string name="face_error_not_enrolled" msgid="1134739108536328412">"Нисте подесили откључавање лицем"</string>
+ <string name="face_error_hw_not_present" msgid="7940978724978763011">"Откључавање лицем није подржано на овом уређају"</string>
+ <string name="face_error_security_update_required" msgid="5076017208528750161">"Сензор је привремено онемогућен."</string>
+ <string name="face_name_template" msgid="3877037340223318119">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string name="face_app_setting_name" msgid="5854024256907828015">"Користите откључавање лицем"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Користите закључавање лицем или закључавање екрана"</string>
+ <string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Потврдите идентитет лицем да бисте наставили"</string>
+ <string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Користите лице или закључавање екрана да бисте наставили"</string>
<string-array name="face_error_vendor">
</string-array>
- <string name="face_error_vendor_unknown" msgid="7387005932083302070">"Došlo je do problema. Probajte ponovo."</string>
- <string name="face_icon_content_description" msgid="465030547475916280">"Ikona lica"</string>
- <string name="permlab_readSyncSettings" msgid="6250532864893156277">"čitanje podešavanja sinhronizacije"</string>
- <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Dozvoljava aplikaciji da čita podešavanja sinhronizacije za nalog. Na primer, ovako može da se utvrdi da li je aplikacija Ljudi sinhronizovana sa nalogom."</string>
- <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"uključivanje i isključivanje sinhronizacije"</string>
- <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Dozvoljava aplikaciji da menja podešavanja sinhronizacije za nalog. Na primer, ovako može da se omogući sinhronizacija aplikacije Ljudi sa nalogom."</string>
- <string name="permlab_readSyncStats" msgid="3747407238320105332">"čitanje statistike o sinhronizaciji"</string>
- <string name="permdesc_readSyncStats" msgid="3867809926567379434">"Dozvoljava aplikaciji da čita statistiku sinhronizacije za nalog, uključujući istoriju sinhronizovanih događaja i količinu podataka koji se sinhronizuju."</string>
- <string name="permlab_sdcardRead" msgid="5791467020950064920">"čitanje sadržaja deljenog memorijskog prostora"</string>
- <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Dozvoljava aplikaciji da čita sadržaj deljenog memorijskog prostora."</string>
- <string name="permlab_readMediaAudio" msgid="8723513075731763810">"čitanje audio fajlova iz deljenog memorijskog prostora"</string>
- <string name="permdesc_readMediaAudio" msgid="5299772574434619399">"Omogućava aplikaciji da čita audio fajlove iz deljenog memorijskog prostora."</string>
- <string name="permlab_readMediaVideo" msgid="7768003311260655007">"čitanje video fajlova iz deljenog memorijskog prostora"</string>
- <string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Omogućava aplikaciji da čita video fajlove iz deljenog memorijskog prostora."</string>
- <string name="permlab_readMediaImages" msgid="4057590631020986789">"čitanje fajlova slika iz deljenog memorijskog prostora"</string>
- <string name="permdesc_readMediaImages" msgid="5836219373138469259">"Omogućava aplikaciji da čita fajlove slika iz deljenog memorijskog prostora."</string>
- <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"čitanje fajlova slika i video snimaka koje korisnik bira iz deljenog memorijskog prostora"</string>
- <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Omogućava aplikaciji da čita fajlove slika i video snimaka koje izaberete iz deljenog memorijskog prostora."</string>
- <string name="permlab_sdcardWrite" msgid="4863021819671416668">"menjanje ili brisanje sadržaja deljenog memorijskog prostora"</string>
- <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Dozvoljava aplikaciji da upisuje sadržaj deljenog memorijskog prostora."</string>
- <string name="permlab_use_sip" msgid="8250774565189337477">"upućivanje/prijem SIP poziva"</string>
- <string name="permdesc_use_sip" msgid="3590270893253204451">"Omogućava aplikaciji da upućuje i prima SIP pozive."</string>
- <string name="permlab_register_sim_subscription" msgid="1653054249287576161">"registruje nove veze sa telekomunikacionim mrežama preko SIM kartice"</string>
- <string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"Dozvoljava aplikaciji da registruje nove veze sa telekomunikacionim mrežama preko SIM kartice."</string>
- <string name="permlab_register_call_provider" msgid="6135073566140050702">"registruje nove veze sa telekomunikacionim mrežama"</string>
- <string name="permdesc_register_call_provider" msgid="4201429251459068613">"Dozvoljava aplikaciji da registruje nove veze sa telekomunikacionim mrežama."</string>
- <string name="permlab_connection_manager" msgid="3179365584691166915">"upravljanje vezama sa telekomunikacionim mrežama"</string>
- <string name="permdesc_connection_manager" msgid="1426093604238937733">"Dozvoljava aplikaciji da upravlja vezama sa telekomunikacionim mrežama."</string>
- <string name="permlab_bind_incall_service" msgid="5990625112603493016">"komuniciraj sa ekranom tokom poziva"</string>
- <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Dozvoljava aplikaciji da kontroliše kada i kako se korisniku prikazuje ekran tokom poziva."</string>
- <string name="permlab_bind_connection_service" msgid="5409268245525024736">"da stupa u interakciju sa telefonskim uslugama"</string>
- <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Dozvoljava interakciju aplikacije sa telefonskim uslugama radi upućivanja/primanja poziva."</string>
- <string name="permlab_control_incall_experience" msgid="6436863486094352987">"pružaj korisnički doživljaj tokom poziva"</string>
- <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"Dozvoljava aplikaciji da pruža korisnički doživljaj tokom poziva."</string>
- <string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"čita istoriju korišćenja mreže"</string>
- <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"Dozvoljava aplikaciji da čita istoriju korišćenja mreže za posebne mreže i aplikacije."</string>
- <string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"upravljanje smernicama za mrežu"</string>
- <string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"Dozvoljava aplikaciji da upravlja smernicama za mrežu i određuje posebna pravila za aplikaciju."</string>
- <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"izmenite obračunavanje korišćenja mreže"</string>
- <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"Dozvoljava aplikaciji da izmeni način na koji aplikacije koriste mrežu. Ne koriste je uobičajene aplikacije."</string>
- <string name="permlab_accessNotifications" msgid="7130360248191984741">"pristup obaveštenjima"</string>
- <string name="permdesc_accessNotifications" msgid="761730149268789668">"Dozvoljava aplikaciji da preuzima, ispituje i briše obaveštenja, uključujući ona koja postavljaju druge aplikacije."</string>
- <string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"povezivanje sa uslugom monitora obaveštenja"</string>
- <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"Dozvoljava vlasniku da se poveže sa interfejsom usluge monitora obaveštenja najvišeg nivoa. Uobičajene aplikacije nikada ne bi trebalo da je koriste."</string>
- <string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"poveži sa uslugom dobavljača uslova"</string>
- <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"Dozvoljava vlasniku da se poveže sa interfejsom najvišeg nivoa usluge dobavljača uslova. Ne bi trebalo nikada da bude potrebno za uobičajene aplikacije."</string>
- <string name="permlab_bindDreamService" msgid="4776175992848982706">"povezivanje sa uslugom sanjarenja"</string>
- <string name="permdesc_bindDreamService" msgid="9129615743300572973">"Dozvoljava vlasniku da se poveže sa interfejsom usluge sanjarenja najvišeg nivoa. Uobičajene aplikacije nikada ne bi trebalo da je koriste."</string>
- <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"pozivanje aplikacije sa konfiguracijom koju određuje operater"</string>
- <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"Dozvoljava vlasniku da poziva aplikaciju sa konfiguracijom koju određuje operater. Uobičajene aplikacije nikada ne bi trebalo da je koriste."</string>
- <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"praćenje podataka o uslovima na mreži"</string>
- <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Dozvoljava aplikaciji da prati podatke o uslovima na mreži. Ne bi nikada trebalo da bude potrebno za normalne aplikacije."</string>
- <string name="permlab_setInputCalibration" msgid="932069700285223434">"promeni kalibraciju ulaznog uređaja"</string>
- <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Dozvoljava aplikaciji da modifikuje parametre kalibracije dodirnog ekrana. Ne bi trebalo da bude potrebno za normalne aplikacije."</string>
- <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"pristup DRM sertifikatima"</string>
- <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Dozvoljava aplikaciji da dodeljuje i koristi DRM sertifikate. Nikada ne bi trebalo da se koristi za uobičajene aplikacije."</string>
- <string name="permlab_handoverStatus" msgid="7620438488137057281">"prijem statusa prebacivanja pomoću Android prebacivanja"</string>
- <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Dozvoljava ovoj aplikaciji da prima informacije o aktuelnim prebacivanjima pomoću Android prebacivanja"</string>
- <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"uklanjaj DRM sertifikate"</string>
- <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Dozvoljava aplikaciji da uklanja DRM sertifikate. Nikada ne bi trebalo da se koristi za obične aplikacije."</string>
- <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"povezivanje sa uslugom za razmenu poruka mobilnog operatera"</string>
- <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Dozvoljava vlasniku da se poveže sa interfejsom najvišeg nivoa za uslugu za razmenu poruka mobilnog operatera. Nikada ne bi trebalo da bude potrebno za standardne aplikacije."</string>
- <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"povezivanje sa uslugama operatera"</string>
- <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Dozvoljava vlasniku da se poveže sa uslugama operatera. Nikada ne bi trebalo da bude potrebno za obične aplikacije."</string>
- <string name="permlab_access_notification_policy" msgid="5524112842876975537">"pristupaj podešavanju Ne uznemiravaj"</string>
- <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Dozvoljava aplikaciji da čita i upisuje konfiguraciju podešavanja Ne uznemiravaj."</string>
- <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"početak korišćenja dozvole za pregled"</string>
- <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da započne korišćenje dozvole za aplikaciju. Nikada ne bi trebalo da bude potrebna za uobičajene aplikacije."</string>
- <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"pokretanje pregleda odluka o dozvolama"</string>
- <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Dozvoljava vlasniku da pokrene ekran za proveru odluka o dozvolama. Nikada ne bi trebalo da bude potrebno za obične aplikacije."</string>
- <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"pokretanje prikaza funkcija aplikacije"</string>
- <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Dozvoljava nosiocu dozvole da započne pregledanje informacija o funkcijama aplikacije."</string>
- <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora pri velikoj brzini uzorkovanja"</string>
- <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Dozvoljava aplikaciji da uzima uzorak podataka senzora pri brzini većoj od 200 Hz"</string>
- <string name="policylab_limitPassword" msgid="4851829918814422199">"Podešavanje pravila za lozinku"</string>
- <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontroliše dužinu i znakove dozvoljene u lozinkama i PIN-ovima za zaključavanje ekrana."</string>
- <string name="policylab_watchLogin" msgid="7599669460083719504">"Nadgledajte pokušaje otključavanja ekrana"</string>
- <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Prati broj netačno unetih lozinki prilikom otključavanja ekrana i zaključava tablet ili briše podatke sa tableta ako je netačna lozinka uneta previše puta."</string>
- <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava Android TV uređaj ili briše sve podatke sa Android TV uređaja ako se unese previše netačnih lozinki."</string>
- <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Prati broj netačno unetih lozinki pri otključavanju ekrana i zaključava sistem za info-zabavu ili briše sve podatke sa sistema za info-zabavu ako je netačna lozinka uneta previše puta."</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Prati broj netačno unetih lozinki pri otključavanju ekrana i zaključava telefon ili briše sve podatke sa telefona ako je netačna lozinka uneta previše puta."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava tablet ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava Android TV uređaj ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava sistem za info-zabavu ili briše sve podatke ovog profila ako se unese previše netačnih lozinki."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava telefon ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
- <string name="policylab_resetPassword" msgid="214556238645096520">"Promena zaključavanja ekrana"</string>
- <string name="policydesc_resetPassword" msgid="4626419138439341851">"Menja zaključavanje ekrana."</string>
- <string name="policylab_forceLock" msgid="7360335502968476434">"Zaključavanje ekrana"</string>
- <string name="policydesc_forceLock" msgid="1008844760853899693">"Kontrola načina i vremena zaključavanja ekrana."</string>
- <string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje svih podataka"</string>
- <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Brisanje podataka na tabletu bez upozorenja resetovanjem na fabrička podešavanja."</string>
- <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Briše podatke Android TV uređaja bez upozorenja pomoću resetovanja na fabrička podešavanja."</string>
- <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Briše podatke na sistemu za info-zabavu bez upozorenja resetovanjem na fabrička podešavanja."</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Brisanje podataka na telefonu bez upozorenja resetovanjem na fabrička podešavanja."</string>
- <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Brisanje podataka profila"</string>
- <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Obriši podatke korisnika"</string>
- <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Briše podatke ovog korisnika na ovom tabletu bez upozorenja."</string>
- <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Briše podatke ovog korisnika na ovom Android TV uređaju bez upozorenja."</string>
- <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Briše podatke ovog profila na ovom sistemu za info-zabavu bez upozorenja."</string>
- <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Briše podatke ovog korisnika na ovom telefonu bez upozorenja."</string>
- <string name="policylab_setGlobalProxy" msgid="215332221188670221">"Podesite globalni proksi server uređaja"</string>
- <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Podešava globalni proksi uređaja koji će se koristiti dok su smernice omogućene. Samo vlasnik uređaja može da podesi globalni proksi."</string>
- <string name="policylab_expirePassword" msgid="6015404400532459169">"Podesi istek. lozin. za zaklj. ekr."</string>
- <string name="policydesc_expirePassword" msgid="9136524319325960675">"Menja koliko često lozinka, PIN ili šablon za zaključavanje ekrana mora da se menja."</string>
- <string name="policylab_encryptedStorage" msgid="9012936958126670110">"Podešavanje šifrovanja skladišta"</string>
- <string name="policydesc_encryptedStorage" msgid="1102516950740375617">"Zahteva da sačuvani podaci aplikacije budu šifrovani."</string>
- <string name="policylab_disableCamera" msgid="5749486347810162018">"Onemogućavanje kamera"</string>
- <string name="policydesc_disableCamera" msgid="3204405908799676104">"Sprečite korišćenje svih kamera uređaja."</string>
- <string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"Onemogućavanje funkcija zaklj. ekrana"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"Sprečava korišćenje nekih funkcija zaključavanja ekrana."</string>
+ <string name="face_error_vendor_unknown" msgid="7387005932083302070">"Дошло је до проблема. Пробајте поново."</string>
+ <string name="face_icon_content_description" msgid="465030547475916280">"Икона лица"</string>
+ <string name="permlab_readSyncSettings" msgid="6250532864893156277">"читање подешавања синхронизације"</string>
+ <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Дозвољава апликацији да чита подешавања синхронизације за налог. На пример, овако може да се утврди да ли је апликација Људи синхронизована са налогом."</string>
+ <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"укључивање и искључивање синхронизације"</string>
+ <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Дозвољава апликацији да мења подешавања синхронизације за налог. На пример, овако може да се омогући синхронизација апликације Људи са налогом."</string>
+ <string name="permlab_readSyncStats" msgid="3747407238320105332">"читање статистике о синхронизацији"</string>
+ <string name="permdesc_readSyncStats" msgid="3867809926567379434">"Дозвољава апликацији да чита статистику синхронизације за налог, укључујући историју синхронизованих догађаја и количину података који се синхронизују."</string>
+ <string name="permlab_sdcardRead" msgid="5791467020950064920">"читање садржаја дељеног меморијског простора"</string>
+ <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Дозвољава апликацији да чита садржај дељеног меморијског простора."</string>
+ <string name="permlab_readMediaAudio" msgid="8723513075731763810">"читање аудио фајлова из дељеног меморијског простора"</string>
+ <string name="permdesc_readMediaAudio" msgid="5299772574434619399">"Омогућава апликацији да чита аудио фајлове из дељеног меморијског простора."</string>
+ <string name="permlab_readMediaVideo" msgid="7768003311260655007">"читање видео фајлова из дељеног меморијског простора"</string>
+ <string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Омогућава апликацији да чита видео фајлове из дељеног меморијског простора."</string>
+ <string name="permlab_readMediaImages" msgid="4057590631020986789">"читање фајлова слика из дељеног меморијског простора"</string>
+ <string name="permdesc_readMediaImages" msgid="5836219373138469259">"Омогућава апликацији да чита фајлове слика из дељеног меморијског простора."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"читање фајлова слика и видео снимака које корисник бира из дељеног меморијског простора"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Омогућава апликацији да чита фајлове слика и видео снимака које изаберете из дељеног меморијског простора."</string>
+ <string name="permlab_sdcardWrite" msgid="4863021819671416668">"мењање или брисање садржаја дељеног меморијског простора"</string>
+ <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Дозвољава апликацији да уписује садржај дељеног меморијског простора."</string>
+ <string name="permlab_use_sip" msgid="8250774565189337477">"упућивање/пријем SIP позива"</string>
+ <string name="permdesc_use_sip" msgid="3590270893253204451">"Омогућава апликацији да упућује и прима SIP позиве."</string>
+ <string name="permlab_register_sim_subscription" msgid="1653054249287576161">"региструје нове везе са телекомуникационим мрежама преко SIM картице"</string>
+ <string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"Дозвољава апликацији да региструје нове везе са телекомуникационим мрежама преко SIM картице."</string>
+ <string name="permlab_register_call_provider" msgid="6135073566140050702">"региструје нове везе са телекомуникационим мрежама"</string>
+ <string name="permdesc_register_call_provider" msgid="4201429251459068613">"Дозвољава апликацији да региструје нове везе са телекомуникационим мрежама."</string>
+ <string name="permlab_connection_manager" msgid="3179365584691166915">"управљање везама са телекомуникационим мрежама"</string>
+ <string name="permdesc_connection_manager" msgid="1426093604238937733">"Дозвољава апликацији да управља везама са телекомуникационим мрежама."</string>
+ <string name="permlab_bind_incall_service" msgid="5990625112603493016">"комуницирај са екраном током позива"</string>
+ <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Дозвољава апликацији да контролише када и како се кориснику приказује екран током позива."</string>
+ <string name="permlab_bind_connection_service" msgid="5409268245525024736">"да ступа у интеракцију са телефонским услугама"</string>
+ <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Дозвољава интеракцију апликације са телефонским услугама ради упућивања/примања позива."</string>
+ <string name="permlab_control_incall_experience" msgid="6436863486094352987">"пружај кориснички доживљај током позива"</string>
+ <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"Дозвољава апликацији да пружа кориснички доживљај током позива."</string>
+ <string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"чита историју коришћења мреже"</string>
+ <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"Дозвољава апликацији да чита историју коришћења мреже за посебне мреже и апликације."</string>
+ <string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"управљање смерницама за мрежу"</string>
+ <string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"Дозвољава апликацији да управља смерницама за мрежу и одређује посебна правила за апликацију."</string>
+ <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"измените обрачунавање коришћења мреже"</string>
+ <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"Дозвољава апликацији да измени начин на који апликације користе мрежу. Не користе је уобичајене апликације."</string>
+ <string name="permlab_accessNotifications" msgid="7130360248191984741">"приступ обавештењима"</string>
+ <string name="permdesc_accessNotifications" msgid="761730149268789668">"Дозвољава апликацији да преузима, испитује и брише обавештења, укључујући она која постављају друге апликације."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"повезивање са услугом монитора обавештења"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"Дозвољава власнику да се повеже са интерфејсом услуге монитора обавештења највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
+ <string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"повежи са услугом добављача услова"</string>
+ <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"Дозвољава власнику да се повеже са интерфејсом највишег нивоа услуге добављача услова. Не би требало никада да буде потребно за уобичајене апликације."</string>
+ <string name="permlab_bindDreamService" msgid="4776175992848982706">"повезивање са услугом сањарења"</string>
+ <string name="permdesc_bindDreamService" msgid="9129615743300572973">"Дозвољава власнику да се повеже са интерфејсом услуге сањарења највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
+ <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"позивање апликације са конфигурацијом коју одређује оператер"</string>
+ <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"Дозвољава власнику да позива апликацију са конфигурацијом коју одређује оператер. Уобичајене апликације никада не би требало да је користе."</string>
+ <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"праћење података о условима на мрежи"</string>
+ <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Дозвољава апликацији да прати податке о условима на мрежи. Не би никада требало да буде потребно за нормалне апликације."</string>
+ <string name="permlab_setInputCalibration" msgid="932069700285223434">"промени калибрацију улазног уређаја"</string>
+ <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Дозвољава апликацији да модификује параметре калибрације додирног екрана. Не би требало да буде потребно за нормалне апликације."</string>
+ <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"приступ DRM сертификатима"</string>
+ <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Дозвољава апликацији да додељује и користи DRM сертификате. Никада не би требало да се користи за уобичајене апликације."</string>
+ <string name="permlab_handoverStatus" msgid="7620438488137057281">"пријем статуса пребацивања помоћу Android пребацивања"</string>
+ <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Дозвољава овој апликацији да прима информације о актуелним пребацивањима помоћу Android пребацивања"</string>
+ <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"уклањај DRM сертификате"</string>
+ <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Дозвољава апликацији да уклања DRM сертификате. Никада не би требало да се користи за обичне апликације."</string>
+ <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"повезивање са услугом за размену порука мобилног оператера"</string>
+ <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Дозвољава власнику да се повеже са интерфејсом највишег нивоа за услугу за размену порука мобилног оператера. Никада не би требало да буде потребно за стандардне апликације."</string>
+ <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"повезивање са услугама оператера"</string>
+ <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Дозвољава власнику да се повеже са услугама оператера. Никада не би требало да буде потребно за обичне апликације."</string>
+ <string name="permlab_access_notification_policy" msgid="5524112842876975537">"приступај подешавању Не узнемиравај"</string>
+ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозвољава апликацији да чита и уписује конфигурацију подешавања Не узнемиравај."</string>
+ <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"почетак коришћења дозволе за преглед"</string>
+ <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозвољава власнику да започне коришћење дозволе за апликацију. Никада не би требало да буде потребна за уобичајене апликације."</string>
+ <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"покретање прегледа одлука о дозволама"</string>
+ <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Дозвољава власнику да покрене екран за проверу одлука о дозволама. Никада не би требало да буде потребно за обичне апликације."</string>
+ <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"покретање приказа функција апликације"</string>
+ <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Дозвољава носиоцу дозволе да започне прегледање информација о функцијама апликације."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"приступ подацима сензора при великој брзини узорковања"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Дозвољава апликацији да узима узорак података сензора при брзини већој од 200 Hz"</string>
+ <string name="policylab_limitPassword" msgid="4851829918814422199">"Подешавање правила за лозинку"</string>
+ <string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролише дужину и знакове дозвољене у лозинкама и PIN-овима за закључавање екрана."</string>
+ <string name="policylab_watchLogin" msgid="7599669460083719504">"Надгледајте покушаје откључавања екрана"</string>
+ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Прати број нетачно унетих лозинки приликом откључавања екрана и закључава таблет или брише податке са таблета ако је нетачна лозинка унета превише пута."</string>
+ <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава Android TV уређај или брише све податке са Android TV уређаја ако се унесе превише нетачних лозинки."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Прати број нетачно унетих лозинки при откључавању екрана и закључава систем за инфо-забаву или брише све податке са система за инфо-забаву ако је нетачна лозинка унета превише пута."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Прати број нетачно унетих лозинки при откључавању екрана и закључава телефон или брише све податке са телефона ако је нетачна лозинка унета превише пута."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава таблет или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава Android TV уређај или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава систем за инфо-забаву или брише све податке овог профила ако се унесе превише нетачних лозинки."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава телефон или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
+ <string name="policylab_resetPassword" msgid="214556238645096520">"Промена закључавања екрана"</string>
+ <string name="policydesc_resetPassword" msgid="4626419138439341851">"Мења закључавање екрана."</string>
+ <string name="policylab_forceLock" msgid="7360335502968476434">"Закључавање екрана"</string>
+ <string name="policydesc_forceLock" msgid="1008844760853899693">"Контрола начина и времена закључавања екрана."</string>
+ <string name="policylab_wipeData" msgid="1359485247727537311">"Брисање свих података"</string>
+ <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Брисање података на таблету без упозорења ресетовањем на фабричка подешавања."</string>
+ <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Брише податке Android TV уређаја без упозорења помоћу ресетовања на фабричка подешавања."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Брише податке на систему за инфо-забаву без упозорења ресетовањем на фабричка подешавања."</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Брисање података на телефону без упозорења ресетовањем на фабричка подешавања."</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Брисање података профила"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Обриши податке корисника"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Брише податке овог корисника на овом таблету без упозорења."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Брише податке овог корисника на овом Android TV уређају без упозорења."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Брише податке овог профила на овом систему за инфо-забаву без упозорења."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Брише податке овог корисника на овом телефону без упозорења."</string>
+ <string name="policylab_setGlobalProxy" msgid="215332221188670221">"Подесите глобални прокси сервер уређаја"</string>
+ <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Подешава глобални прокси уређаја који ће се користити док су смернице омогућене. Само власник уређаја може да подеси глобални прокси."</string>
+ <string name="policylab_expirePassword" msgid="6015404400532459169">"Подеси истек. лозин. за закљ. екр."</string>
+ <string name="policydesc_expirePassword" msgid="9136524319325960675">"Мења колико често лозинка, PIN или шаблон за закључавање екрана мора да се мења."</string>
+ <string name="policylab_encryptedStorage" msgid="9012936958126670110">"Подешавање шифровања складишта"</string>
+ <string name="policydesc_encryptedStorage" msgid="1102516950740375617">"Захтева да сачувани подаци апликације буду шифровани."</string>
+ <string name="policylab_disableCamera" msgid="5749486347810162018">"Онемогућавање камера"</string>
+ <string name="policydesc_disableCamera" msgid="3204405908799676104">"Спречите коришћење свих камера уређаја."</string>
+ <string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"Онемогућавање функција закљ. екрана"</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"Спречава коришћење неких функција закључавања екрана."</string>
<string-array name="phoneTypes">
- <item msgid="8996339953292723951">"Kuća"</item>
- <item msgid="7740243458912727194">"Mobilni"</item>
- <item msgid="8526146065496663766">"Posao"</item>
- <item msgid="8150904584178569699">"Faks na poslu"</item>
- <item msgid="4537253139152229577">"Faks kod kuće"</item>
- <item msgid="6751245029698664340">"Pejdžer"</item>
- <item msgid="1692790665884224905">"Drugo"</item>
- <item msgid="6216981255272016212">"Prilagođeno"</item>
+ <item msgid="8996339953292723951">"Кућа"</item>
+ <item msgid="7740243458912727194">"Мобилни"</item>
+ <item msgid="8526146065496663766">"Посао"</item>
+ <item msgid="8150904584178569699">"Факс на послу"</item>
+ <item msgid="4537253139152229577">"Факс код куће"</item>
+ <item msgid="6751245029698664340">"Пејџер"</item>
+ <item msgid="1692790665884224905">"Друго"</item>
+ <item msgid="6216981255272016212">"Прилагођено"</item>
</string-array>
<string-array name="emailAddressTypes">
- <item msgid="7786349763648997741">"Kuća"</item>
- <item msgid="435564470865989199">"Posao"</item>
- <item msgid="4199433197875490373">"Drugo"</item>
- <item msgid="3233938986670468328">"Prilagođeno"</item>
+ <item msgid="7786349763648997741">"Кућа"</item>
+ <item msgid="435564470865989199">"Посао"</item>
+ <item msgid="4199433197875490373">"Друго"</item>
+ <item msgid="3233938986670468328">"Прилагођено"</item>
</string-array>
<string-array name="postalAddressTypes">
- <item msgid="3861463339764243038">"Kuća"</item>
- <item msgid="5472578890164979109">"Posao"</item>
- <item msgid="5718921296646594739">"Drugo"</item>
- <item msgid="5523122236731783179">"Prilagođeno"</item>
+ <item msgid="3861463339764243038">"Кућа"</item>
+ <item msgid="5472578890164979109">"Посао"</item>
+ <item msgid="5718921296646594739">"Друго"</item>
+ <item msgid="5523122236731783179">"Прилагођено"</item>
</string-array>
<string-array name="imAddressTypes">
- <item msgid="588088543406993772">"Kuća"</item>
- <item msgid="5503060422020476757">"Posao"</item>
- <item msgid="2530391194653760297">"Drugo"</item>
- <item msgid="7640927178025203330">"Prilagođeno"</item>
+ <item msgid="588088543406993772">"Кућа"</item>
+ <item msgid="5503060422020476757">"Посао"</item>
+ <item msgid="2530391194653760297">"Друго"</item>
+ <item msgid="7640927178025203330">"Прилагођено"</item>
</string-array>
<string-array name="organizationTypes">
- <item msgid="6144047813304847762">"Posao"</item>
- <item msgid="7402720230065674193">"Drugo"</item>
- <item msgid="808230403067569648">"Prilagođeno"</item>
+ <item msgid="6144047813304847762">"Посао"</item>
+ <item msgid="7402720230065674193">"Друго"</item>
+ <item msgid="808230403067569648">"Прилагођено"</item>
</string-array>
<string-array name="imProtocols">
<item msgid="7535761744432206400">"AIM"</item>
@@ -871,45 +871,45 @@
<item msgid="4717545739447438044">"ICQ"</item>
<item msgid="8293711853624033835">"Jabber"</item>
</string-array>
- <string name="phoneTypeCustom" msgid="5120365721260686814">"Prilagođeno"</string>
- <string name="phoneTypeHome" msgid="3880132427643623588">"Kuća"</string>
- <string name="phoneTypeMobile" msgid="1178852541462086735">"Mobilni"</string>
- <string name="phoneTypeWork" msgid="6604967163358864607">"Posao"</string>
- <string name="phoneTypeFaxWork" msgid="6757519896109439123">"Faks na poslu"</string>
- <string name="phoneTypeFaxHome" msgid="6678559953115904345">"Faks kod kuće"</string>
- <string name="phoneTypePager" msgid="576402072263522767">"Pejdžer"</string>
- <string name="phoneTypeOther" msgid="6918196243648754715">"Drugo"</string>
- <string name="phoneTypeCallback" msgid="3455781500844157767">"Povratni poziv"</string>
- <string name="phoneTypeCar" msgid="4604775148963129195">"Automobil"</string>
- <string name="phoneTypeCompanyMain" msgid="4482773154536455441">"Poslovni glavni"</string>
+ <string name="phoneTypeCustom" msgid="5120365721260686814">"Прилагођено"</string>
+ <string name="phoneTypeHome" msgid="3880132427643623588">"Кућа"</string>
+ <string name="phoneTypeMobile" msgid="1178852541462086735">"Мобилни"</string>
+ <string name="phoneTypeWork" msgid="6604967163358864607">"Посао"</string>
+ <string name="phoneTypeFaxWork" msgid="6757519896109439123">"Факс на послу"</string>
+ <string name="phoneTypeFaxHome" msgid="6678559953115904345">"Факс код куће"</string>
+ <string name="phoneTypePager" msgid="576402072263522767">"Пејџер"</string>
+ <string name="phoneTypeOther" msgid="6918196243648754715">"Друго"</string>
+ <string name="phoneTypeCallback" msgid="3455781500844157767">"Повратни позив"</string>
+ <string name="phoneTypeCar" msgid="4604775148963129195">"Аутомобил"</string>
+ <string name="phoneTypeCompanyMain" msgid="4482773154536455441">"Пословни главни"</string>
<string name="phoneTypeIsdn" msgid="2496238954533998512">"ISDN"</string>
- <string name="phoneTypeMain" msgid="5199722006991000111">"Glavni"</string>
- <string name="phoneTypeOtherFax" msgid="3037145630364770357">"Drugi faks"</string>
- <string name="phoneTypeRadio" msgid="2637819130239264771">"Radio"</string>
- <string name="phoneTypeTelex" msgid="2558783611711876562">"Teleks"</string>
+ <string name="phoneTypeMain" msgid="5199722006991000111">"Главни"</string>
+ <string name="phoneTypeOtherFax" msgid="3037145630364770357">"Други факс"</string>
+ <string name="phoneTypeRadio" msgid="2637819130239264771">"Радио"</string>
+ <string name="phoneTypeTelex" msgid="2558783611711876562">"Телекс"</string>
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
- <string name="phoneTypeWorkMobile" msgid="7522314392003565121">"Poslovni mobilni"</string>
- <string name="phoneTypeWorkPager" msgid="3748332310638505234">"Poslovni pejdžer"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"Pomoćnik"</string>
+ <string name="phoneTypeWorkMobile" msgid="7522314392003565121">"Пословни мобилни"</string>
+ <string name="phoneTypeWorkPager" msgid="3748332310638505234">"Пословни пејџер"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"Помоћник"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
- <string name="eventTypeCustom" msgid="3257367158986466481">"Prilagođeno"</string>
- <string name="eventTypeBirthday" msgid="7770026752793912283">"Rođendan"</string>
- <string name="eventTypeAnniversary" msgid="4684702412407916888">"Godišnjica"</string>
- <string name="eventTypeOther" msgid="530671238533887997">"Drugi"</string>
- <string name="emailTypeCustom" msgid="1809435350482181786">"Prilagođeno"</string>
- <string name="emailTypeHome" msgid="1597116303154775999">"Kuća"</string>
- <string name="emailTypeWork" msgid="2020095414401882111">"Posao"</string>
- <string name="emailTypeOther" msgid="5131130857030897465">"Drugo"</string>
- <string name="emailTypeMobile" msgid="787155077375364230">"Mobilni"</string>
- <string name="postalTypeCustom" msgid="5645590470242939129">"Prilagođeno"</string>
- <string name="postalTypeHome" msgid="7562272480949727912">"Kuća"</string>
- <string name="postalTypeWork" msgid="8553425424652012826">"Posao"</string>
- <string name="postalTypeOther" msgid="7094245413678857420">"Drugo"</string>
- <string name="imTypeCustom" msgid="5653384545085765570">"Prilagođeno"</string>
- <string name="imTypeHome" msgid="6996507981044278216">"Kuća"</string>
- <string name="imTypeWork" msgid="2099668940169903123">"Posao"</string>
- <string name="imTypeOther" msgid="8068447383276219810">"Drugo"</string>
- <string name="imProtocolCustom" msgid="4437878287653764692">"Prilagođeno"</string>
+ <string name="eventTypeCustom" msgid="3257367158986466481">"Прилагођено"</string>
+ <string name="eventTypeBirthday" msgid="7770026752793912283">"Рођендан"</string>
+ <string name="eventTypeAnniversary" msgid="4684702412407916888">"Годишњица"</string>
+ <string name="eventTypeOther" msgid="530671238533887997">"Други"</string>
+ <string name="emailTypeCustom" msgid="1809435350482181786">"Прилагођено"</string>
+ <string name="emailTypeHome" msgid="1597116303154775999">"Кућа"</string>
+ <string name="emailTypeWork" msgid="2020095414401882111">"Посао"</string>
+ <string name="emailTypeOther" msgid="5131130857030897465">"Друго"</string>
+ <string name="emailTypeMobile" msgid="787155077375364230">"Мобилни"</string>
+ <string name="postalTypeCustom" msgid="5645590470242939129">"Прилагођено"</string>
+ <string name="postalTypeHome" msgid="7562272480949727912">"Кућа"</string>
+ <string name="postalTypeWork" msgid="8553425424652012826">"Посао"</string>
+ <string name="postalTypeOther" msgid="7094245413678857420">"Друго"</string>
+ <string name="imTypeCustom" msgid="5653384545085765570">"Прилагођено"</string>
+ <string name="imTypeHome" msgid="6996507981044278216">"Кућа"</string>
+ <string name="imTypeWork" msgid="2099668940169903123">"Посао"</string>
+ <string name="imTypeOther" msgid="8068447383276219810">"Друго"</string>
+ <string name="imProtocolCustom" msgid="4437878287653764692">"Прилагођено"</string>
<string name="imProtocolAim" msgid="4050198236506604378">"AIM"</string>
<string name="imProtocolMsn" msgid="2257148557766499232">"Windows Live"</string>
<string name="imProtocolYahoo" msgid="5373338758093392231">"Yahoo"</string>
@@ -919,51 +919,51 @@
<string name="imProtocolIcq" msgid="2410325380427389521">"ICQ"</string>
<string name="imProtocolJabber" msgid="7919269388889582015">"Jabber"</string>
<string name="imProtocolNetMeeting" msgid="4985002408136148256">"NetMeeting"</string>
- <string name="orgTypeWork" msgid="8684458700669564172">"Posao"</string>
- <string name="orgTypeOther" msgid="5450675258408005553">"Drugo"</string>
- <string name="orgTypeCustom" msgid="1126322047677329218">"Prilagođeno"</string>
- <string name="relationTypeCustom" msgid="282938315217441351">"Prilagođeno"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"Pomoćnik"</string>
- <string name="relationTypeBrother" msgid="7141662427379247820">"Brat"</string>
- <string name="relationTypeChild" msgid="9076258911292693601">"Dete"</string>
- <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Nevenčani partner"</string>
- <string name="relationTypeFather" msgid="3856225062864790596">"Otac"</string>
- <string name="relationTypeFriend" msgid="3192092625893980574">"Prijatelj"</string>
- <string name="relationTypeManager" msgid="2272860813153171857">"Menadžer"</string>
- <string name="relationTypeMother" msgid="2331762740982699460">"Majka"</string>
- <string name="relationTypeParent" msgid="4177920938333039882">"Roditelj"</string>
- <string name="relationTypePartner" msgid="4018017075116766194">"Partner"</string>
- <string name="relationTypeReferredBy" msgid="5285082289602849400">"Uputio/la"</string>
- <string name="relationTypeRelative" msgid="3396498519818009134">"Rođak"</string>
- <string name="relationTypeSister" msgid="3721676005094140671">"Sestra"</string>
- <string name="relationTypeSpouse" msgid="6916682664436031703">"Suprug/a"</string>
- <string name="sipAddressTypeCustom" msgid="6283889809842649336">"Prilagođeno"</string>
- <string name="sipAddressTypeHome" msgid="5918441930656878367">"Početna"</string>
- <string name="sipAddressTypeWork" msgid="7873967986701216770">"Posao"</string>
- <string name="sipAddressTypeOther" msgid="6317012577345187275">"Drugi"</string>
- <string name="quick_contacts_not_available" msgid="1262709196045052223">"Nije pronađena nijedna aplikacija za prikaz ovog kontakta."</string>
- <string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Unesite PIN kôd"</string>
- <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Unesite PUK i novi PIN kôd"</string>
- <string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"PUK kôd"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"Novi PIN kôd"</string>
- <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Dodirnite za unos lozinke"</font></string>
- <string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"Otkucajte lozinku da biste otključali"</string>
- <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Unesite PIN za otključavanje"</string>
- <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"PIN kôd je netačan."</string>
- <string name="keyguard_label_text" msgid="3841953694564168384">"Da biste otključali, pritisnite „Meni“, a zatim 0."</string>
- <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Broj za hitne slučajeve"</string>
- <string name="lockscreen_carrier_default" msgid="6192313772955399160">"Mobilna mreža nije dostupna"</string>
- <string name="lockscreen_screen_locked" msgid="7364905540516041817">"Ekran je zaključan."</string>
- <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite „Meni“ da biste otključali telefon ili uputite hitan poziv."</string>
- <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite „Meni“ za otključavanje."</string>
- <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Unesite šablon za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitne službe"</string>
- <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazad na poziv"</string>
- <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tačno!"</string>
- <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probajte ponovo"</string>
- <string name="lockscreen_password_wrong" msgid="8605355913868947490">"Probajte ponovo"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"Otključaj za sve funkcije i podatke"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Premašen je najveći dozvoljeni broj pokušaja Otključavanja licem"</string>
+ <string name="orgTypeWork" msgid="8684458700669564172">"Посао"</string>
+ <string name="orgTypeOther" msgid="5450675258408005553">"Друго"</string>
+ <string name="orgTypeCustom" msgid="1126322047677329218">"Прилагођено"</string>
+ <string name="relationTypeCustom" msgid="282938315217441351">"Прилагођено"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"Помоћник"</string>
+ <string name="relationTypeBrother" msgid="7141662427379247820">"Брат"</string>
+ <string name="relationTypeChild" msgid="9076258911292693601">"Дете"</string>
+ <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Невенчани партнер"</string>
+ <string name="relationTypeFather" msgid="3856225062864790596">"Отац"</string>
+ <string name="relationTypeFriend" msgid="3192092625893980574">"Пријатељ"</string>
+ <string name="relationTypeManager" msgid="2272860813153171857">"Менаџер"</string>
+ <string name="relationTypeMother" msgid="2331762740982699460">"Мајка"</string>
+ <string name="relationTypeParent" msgid="4177920938333039882">"Родитељ"</string>
+ <string name="relationTypePartner" msgid="4018017075116766194">"Партнер"</string>
+ <string name="relationTypeReferredBy" msgid="5285082289602849400">"Упутио/ла"</string>
+ <string name="relationTypeRelative" msgid="3396498519818009134">"Рођак"</string>
+ <string name="relationTypeSister" msgid="3721676005094140671">"Сестра"</string>
+ <string name="relationTypeSpouse" msgid="6916682664436031703">"Супруг/а"</string>
+ <string name="sipAddressTypeCustom" msgid="6283889809842649336">"Прилагођено"</string>
+ <string name="sipAddressTypeHome" msgid="5918441930656878367">"Почетна"</string>
+ <string name="sipAddressTypeWork" msgid="7873967986701216770">"Посао"</string>
+ <string name="sipAddressTypeOther" msgid="6317012577345187275">"Други"</string>
+ <string name="quick_contacts_not_available" msgid="1262709196045052223">"Није пронађена ниједна апликација за приказ овог контакта."</string>
+ <string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Унесите PIN кôд"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Унесите PUK и нови PIN кôд"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"PUK кôд"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"Нови PIN кôд"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Додирните за унос лозинке"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"Откуцајте лозинку да бисте откључали"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Унесите PIN за откључавање"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"PIN кôд је нетачан."</string>
+ <string name="keyguard_label_text" msgid="3841953694564168384">"Да бисте откључали, притисните „Мени“, а затим 0."</string>
+ <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Број за хитне случајеве"</string>
+ <string name="lockscreen_carrier_default" msgid="6192313772955399160">"Мобилна мрежа није доступна"</string>
+ <string name="lockscreen_screen_locked" msgid="7364905540516041817">"Екран је закључан."</string>
+ <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
+ <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притисните „Мени“ за откључавање."</string>
+ <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Унесите шаблон за откључавање"</string>
+ <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Хитне службе"</string>
+ <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад на позив"</string>
+ <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Тачно!"</string>
+ <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Пробајте поново"</string>
+ <string name="lockscreen_password_wrong" msgid="8605355913868947490">"Пробајте поново"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"Откључај за све функције и податке"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Премашен је највећи дозвољени број покушаја Откључавања лицем"</string>
<!-- no translation found for lockscreen_missing_sim_message_short (1229301273156907613) -->
<skip />
<!-- no translation found for lockscreen_missing_sim_message (3986843848305639161) -->
@@ -980,804 +980,804 @@
<skip />
<!-- no translation found for lockscreen_permanent_disabled_sim_instructions (6902979937802238429) -->
<skip />
- <string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Prethodna pesma"</string>
- <string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Sledeća pesma"</string>
- <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pauza"</string>
- <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Pusti"</string>
- <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Zaustavi"</string>
- <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Premotaj unazad"</string>
- <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Premotaj unapred"</string>
- <string name="emergency_calls_only" msgid="3057351206678279851">"Samo hitni pozivi"</string>
- <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Mreža je zaključana"</string>
+ <string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Претходна песма"</string>
+ <string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Следећа песма"</string>
+ <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Пауза"</string>
+ <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Пусти"</string>
+ <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Заустави"</string>
+ <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Премотај уназад"</string>
+ <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Премотај унапред"</string>
+ <string name="emergency_calls_only" msgid="3057351206678279851">"Само хитни позиви"</string>
+ <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Мрежа је закључана"</string>
<!-- no translation found for lockscreen_sim_puk_locked_message (2867953953604224166) -->
<skip />
- <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Pogledajte Korisnički vodič ili kontaktirajte Korisničku podršku."</string>
+ <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Погледајте Кориснички водич или контактирајте Корисничку подршку."</string>
<!-- no translation found for lockscreen_sim_locked_message (5911944931911850164) -->
<skip />
<!-- no translation found for lockscreen_sim_unlock_progress_dialog_message (8381565919325410939) -->
<skip />
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"<xliff:g id="NUMBER_0">%1$d</xliff:g> puta ste nepravilno nacrtali šablon za otključavanje. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunde/i."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"<xliff:g id="NUMBER_0">%1$d</xliff:g> puta ste pogrešno uneli lozinku. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunde/i."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"<xliff:g id="NUMBER_0">%1$d</xliff:g> puta ste pogrešno uneli PIN. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunde/i."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"<xliff:g id="NUMBER_0">%1$d</xliff:g> puta ste netačno uneli šablon za otključavanje. Nakon još <xliff:g id="NUMBER_1">%2$d</xliff:g> nesupešna(ih) pokušaja, od vas će biti zatraženo da otključate tablet pomoću podataka za prijavljivanje na Google.\n\n Probajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunde/i."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Netačno ste nacrtali šablon za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još puta (<xliff:g id="NUMBER_1">%2$d</xliff:g>), zatražićemo da otključate telefon pomoću Android TV uređaja.\n\n Probajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"<xliff:g id="NUMBER_0">%1$d</xliff:g> puta ste netačno uneli šablon za otključavanje. Nakon još <xliff:g id="NUMBER_1">%2$d</xliff:g> nesupešna(ih) pokušaja, od vas će biti zatraženo da otključate telefon pomoću podataka za prijavljivanje na Google.\n\n Probajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunde/i."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Nepravilno ste pokušali da otključate tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još neuspešnih pokušaja (<xliff:g id="NUMBER_1">%2$d</xliff:g>) tablet će biti resetovan na fabrička podešavanja i svi korisnički podaci će biti izgubljeni."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Broj vaših neuspešnih pokušaja da otključate Android TV uređaj: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Broj preostalih neuspešnih pokušaja posle kojih će se Android TV resetovati na fabrička podešavanja i svi podaci korisnika će biti izgubljeni: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Neispravno ste pokušali da otključate telefon <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još neuspešnih pokušaja (<xliff:g id="NUMBER_1">%2$d</xliff:g>) telefon će biti resetovan na fabrička podešavanja i svi korisnički podaci će biti izgubljeni."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Neispravno ste pokušali da otključate tablet <xliff:g id="NUMBER">%d</xliff:g> puta. Tablet će sada biti vraćen na podrazumevana fabrička podešavanja."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Broj vaših neuspešnih pokušaja da otključate Android TV uređaj: <xliff:g id="NUMBER">%d</xliff:g>. Android TV uređaj će se sada resetovati na fabrička podešavanja."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Neispravno ste pokušali da otključate telefon <xliff:g id="NUMBER">%d</xliff:g> puta. Telefon će sada biti vraćen na podrazumevana fabrička podešavanja."</string>
- <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Probajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekunde/i."</string>
- <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Zaboravili ste šablon?"</string>
- <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Otključavanje naloga"</string>
- <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Previše pokušaja unosa šablona"</string>
- <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Da biste otključali, prijavite se pomoću Google naloga."</string>
- <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Korisničko ime (imejl adresa)"</string>
- <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Lozinka"</string>
- <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Prijavi me"</string>
- <string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"Nevažeće korisničko ime ili lozinka."</string>
- <string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"Zaboravili ste korisničko ime ili lozinku?\nPosetite adresu "<b>"google.com/accounts/recovery"</b>"."</string>
- <string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"Proveravanje..."</string>
- <string name="lockscreen_unlock_label" msgid="4648257878373307582">"Otključaj"</string>
- <string name="lockscreen_sound_on_label" msgid="1660281470535492430">"Uključi zvuk"</string>
- <string name="lockscreen_sound_off_label" msgid="2331496559245450053">"Isključi zvuk"</string>
- <string name="lockscreen_access_pattern_start" msgid="3778502525702613399">"Obrazac je započet"</string>
- <string name="lockscreen_access_pattern_cleared" msgid="7493849102641167049">"Obrazac je obrisan"</string>
- <string name="lockscreen_access_pattern_cell_added" msgid="6746676335293144163">"Ćelija je dodata"</string>
- <string name="lockscreen_access_pattern_cell_added_verbose" msgid="2931364927622563465">"Ćelija <xliff:g id="CELL_INDEX">%1$s</xliff:g> je dodata"</string>
- <string name="lockscreen_access_pattern_detected" msgid="3931150554035194012">"Obrazac je dovršen"</string>
- <string name="lockscreen_access_pattern_area" msgid="1288780416685002841">"Oblast šablona."</string>
- <string name="keyguard_accessibility_widget_changed" msgid="7298011259508200234">"%1$s. Vidžet %2$d od %3$d."</string>
- <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"Dodaj vidžet."</string>
- <string name="keyguard_accessibility_widget_empty_slot" msgid="544239307077644480">"Prazno"</string>
- <string name="keyguard_accessibility_unlock_area_expanded" msgid="7768634718706488951">"Oblast otključavanja je proširena."</string>
- <string name="keyguard_accessibility_unlock_area_collapsed" msgid="4729922043778400434">"Oblast otključavanja je skupljena."</string>
- <string name="keyguard_accessibility_widget" msgid="6776892679715699875">"Vidžet <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
- <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"Izbor korisnika"</string>
- <string name="keyguard_accessibility_status" msgid="6792745049712397237">"Status"</string>
- <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Kamera"</string>
- <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Kontrole za medije"</string>
- <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"Započela je promena redosleda vidžeta."</string>
- <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Promena redosleda vidžeta je završena."</string>
- <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Vidžet <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> je izbrisan."</string>
- <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Proširi oblast otključavanja."</string>
- <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Otključavanje prevlačenjem."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Otključavanje šablonom."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Otključavanje licem."</string>
- <string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Otključavanje PIN-om."</string>
- <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Otključava SIM karticu PIN-om."</string>
- <string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Otključava SIM karticu PUK-om."</string>
- <string name="keyguard_accessibility_password_unlock" msgid="6130186108581153265">"Otključavanje lozinkom."</string>
- <string name="keyguard_accessibility_pattern_area" msgid="1419570880512350689">"Oblast šablona."</string>
- <string name="keyguard_accessibility_slide_area" msgid="4331399051142520176">"Oblast prevlačenja."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте неправилно нацртали шаблон за откључавање. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде/и."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте погрешно унели лозинку. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде/и."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте погрешно унели PIN. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде/и."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте нетачно унели шаблон за откључавање. Након још <xliff:g id="NUMBER_1">%2$d</xliff:g> несупешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу података за пријављивање на Google.\n\n Пробајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунде/и."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Нетачно сте нацртали шаблон за откључавање <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још пута (<xliff:g id="NUMBER_1">%2$d</xliff:g>), затражићемо да откључате телефон помоћу Android TV уређаја.\n\n Пробајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"<xliff:g id="NUMBER_0">%1$d</xliff:g> пута сте нетачно унели шаблон за откључавање. Након још <xliff:g id="NUMBER_1">%2$d</xliff:g> несупешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу података за пријављивање на Google.\n\n Пробајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунде/и."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Неправилно сте покушали да откључате таблет <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%2$d</xliff:g>) таблет ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Број ваших неуспешних покушаја да откључате Android TV уређај: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Број преосталих неуспешних покушаја после којих ће се Android TV ресетовати на фабричка подешавања и сви подаци корисника ће бити изгубљени: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Неисправно сте покушали да откључате телефон <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%2$d</xliff:g>) телефон ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Неисправно сте покушали да откључате таблет <xliff:g id="NUMBER">%d</xliff:g> пута. Таблет ће сада бити враћен на подразумевана фабричка подешавања."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Број ваших неуспешних покушаја да откључате Android TV уређај: <xliff:g id="NUMBER">%d</xliff:g>. Android TV уређај ће се сада ресетовати на фабричка подешавања."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Неисправно сте покушали да откључате телефон <xliff:g id="NUMBER">%d</xliff:g> пута. Телефон ће сада бити враћен на подразумевана фабричка подешавања."</string>
+ <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Пробајте поново за <xliff:g id="NUMBER">%d</xliff:g> секунде/и."</string>
+ <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Заборавили сте шаблон?"</string>
+ <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Откључавање налога"</string>
+ <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Превише покушаја уноса шаблона"</string>
+ <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Да бисте откључали, пријавите се помоћу Google налога."</string>
+ <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Корисничко име (имејл адреса)"</string>
+ <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Лозинка"</string>
+ <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Пријави ме"</string>
+ <string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"Неважеће корисничко име или лозинка."</string>
+ <string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"Заборавили сте корисничко име или лозинку?\nПосетите адресу "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"Проверавање..."</string>
+ <string name="lockscreen_unlock_label" msgid="4648257878373307582">"Откључај"</string>
+ <string name="lockscreen_sound_on_label" msgid="1660281470535492430">"Укључи звук"</string>
+ <string name="lockscreen_sound_off_label" msgid="2331496559245450053">"Искључи звук"</string>
+ <string name="lockscreen_access_pattern_start" msgid="3778502525702613399">"Образац је започет"</string>
+ <string name="lockscreen_access_pattern_cleared" msgid="7493849102641167049">"Образац је обрисан"</string>
+ <string name="lockscreen_access_pattern_cell_added" msgid="6746676335293144163">"Ћелија је додата"</string>
+ <string name="lockscreen_access_pattern_cell_added_verbose" msgid="2931364927622563465">"Ћелија <xliff:g id="CELL_INDEX">%1$s</xliff:g> је додата"</string>
+ <string name="lockscreen_access_pattern_detected" msgid="3931150554035194012">"Образац је довршен"</string>
+ <string name="lockscreen_access_pattern_area" msgid="1288780416685002841">"Област шаблона."</string>
+ <string name="keyguard_accessibility_widget_changed" msgid="7298011259508200234">"%1$s. Виџет %2$d од %3$d."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"Додај виџет."</string>
+ <string name="keyguard_accessibility_widget_empty_slot" msgid="544239307077644480">"Празно"</string>
+ <string name="keyguard_accessibility_unlock_area_expanded" msgid="7768634718706488951">"Област откључавања је проширена."</string>
+ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="4729922043778400434">"Област откључавања је скупљена."</string>
+ <string name="keyguard_accessibility_widget" msgid="6776892679715699875">"Виџет <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+ <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"Избор корисника"</string>
+ <string name="keyguard_accessibility_status" msgid="6792745049712397237">"Статус"</string>
+ <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Камера"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Контроле за медије"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"Започела је промена редоследа виџета."</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Промена редоследа виџета је завршена."</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Виџет <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> је избрисан."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Прошири област откључавања."</string>
+ <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Откључавање превлачењем."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Откључавање шаблоном."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Откључавање лицем."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Откључавање PIN-ом."</string>
+ <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Откључава SIM картицу PIN-ом."</string>
+ <string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Откључава SIM картицу PUK-ом."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="6130186108581153265">"Откључавање лозинком."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="1419570880512350689">"Област шаблона."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="4331399051142520176">"Област превлачења."</string>
<string name="password_keyboard_label_symbol_key" msgid="2716255580853511949">"?123"</string>
<string name="password_keyboard_label_alpha_key" msgid="5294837425652726684">"ABC"</string>
<string name="password_keyboard_label_alt_key" msgid="8528261816395508841">"ALT"</string>
- <string name="granularity_label_character" msgid="8903387663153706317">"znak"</string>
- <string name="granularity_label_word" msgid="3686589158760620518">"reč"</string>
- <string name="granularity_label_link" msgid="9007852307112046526">"link"</string>
- <string name="granularity_label_line" msgid="376204904280620221">"red"</string>
- <string name="factorytest_failed" msgid="3190979160945298006">"Fabričko testiranje nije uspelo"</string>
- <string name="factorytest_not_system" msgid="5658160199925519869">"Radnja FACTORY_TEST je podržana samo za pakete instalirane u folderu /system/app."</string>
- <string name="factorytest_no_action" msgid="339252838115675515">"Nije pronađen nijedan paket koji obezbeđuje radnju FACTORY_TEST."</string>
- <string name="factorytest_reboot" msgid="2050147445567257365">"Restartuj"</string>
- <string name="js_dialog_title" msgid="7464775045615023241">"Na stranici na adresi „<xliff:g id="TITLE">%s</xliff:g>“ piše:"</string>
+ <string name="granularity_label_character" msgid="8903387663153706317">"знак"</string>
+ <string name="granularity_label_word" msgid="3686589158760620518">"реч"</string>
+ <string name="granularity_label_link" msgid="9007852307112046526">"линк"</string>
+ <string name="granularity_label_line" msgid="376204904280620221">"ред"</string>
+ <string name="factorytest_failed" msgid="3190979160945298006">"Фабричко тестирање није успело"</string>
+ <string name="factorytest_not_system" msgid="5658160199925519869">"Радња FACTORY_TEST је подржана само за пакете инсталиране у фолдеру /system/app."</string>
+ <string name="factorytest_no_action" msgid="339252838115675515">"Није пронађен ниједан пакет који обезбеђује радњу FACTORY_TEST."</string>
+ <string name="factorytest_reboot" msgid="2050147445567257365">"Рестартуј"</string>
+ <string name="js_dialog_title" msgid="7464775045615023241">"На страници на адреси „<xliff:g id="TITLE">%s</xliff:g>“ пише:"</string>
<string name="js_dialog_title_default" msgid="3769524569903332476">"JavaScript"</string>
- <string name="js_dialog_before_unload_title" msgid="7012587995876771246">"Potvrda navigacije"</string>
- <string name="js_dialog_before_unload_positive_button" msgid="4274257182303565509">"Zatvori ovu stranicu"</string>
- <string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"Ostani na ovoj stranici"</string>
- <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nDa li stvarno želite da napustite ovu stranicu?"</string>
- <string name="save_password_label" msgid="9161712335355510035">"Potvrda"</string>
- <string name="double_tap_toast" msgid="7065519579174882778">"Savet: Dodirnite dvaput da biste uvećali i umanjili prikaz."</string>
- <string name="autofill_this_form" msgid="3187132440451621492">"Autom. pop."</string>
- <string name="setup_autofill" msgid="5431369130866618567">"Podeš. aut. pop."</string>
- <string name="autofill_window_title" msgid="4379134104008111961">"Automatski popunjava <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
+ <string name="js_dialog_before_unload_title" msgid="7012587995876771246">"Потврда навигације"</string>
+ <string name="js_dialog_before_unload_positive_button" msgid="4274257182303565509">"Затвори ову страницу"</string>
+ <string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"Остани на овој страници"</string>
+ <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nДа ли стварно желите да напустите ову страницу?"</string>
+ <string name="save_password_label" msgid="9161712335355510035">"Потврда"</string>
+ <string name="double_tap_toast" msgid="7065519579174882778">"Савет: Додирните двапут да бисте увећали и умањили приказ."</string>
+ <string name="autofill_this_form" msgid="3187132440451621492">"Аутом. поп."</string>
+ <string name="setup_autofill" msgid="5431369130866618567">"Подеш. аут. поп."</string>
+ <string name="autofill_window_title" msgid="4379134104008111961">"Аутоматски попуњава <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>
<string name="autofill_address_summary_format" msgid="8417010069362125194">"$1$2$3"</string>
- <string name="autofill_province" msgid="3676846437741893159">"Pokrajina"</string>
- <string name="autofill_postal_code" msgid="7034789388968295591">"Poštanski broj"</string>
- <string name="autofill_state" msgid="3341725337190434069">"Država"</string>
- <string name="autofill_zip_code" msgid="1315503730274962450">"Poštanski broj"</string>
- <string name="autofill_county" msgid="7781382735643492173">"Okrug"</string>
- <string name="autofill_island" msgid="5367139008536593734">"Ostrvo"</string>
- <string name="autofill_district" msgid="6428712062213557327">"Distrikt"</string>
- <string name="autofill_department" msgid="9047276226873531529">"Odeljenje"</string>
- <string name="autofill_prefecture" msgid="7267397763720241872">"Prefektura"</string>
- <string name="autofill_parish" msgid="6847960518334530198">"Parohija"</string>
- <string name="autofill_area" msgid="8289022370678448983">"Oblast"</string>
- <string name="autofill_emirate" msgid="2544082046790551168">"Emirat"</string>
- <string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"čitanje veb obeleživača i istorije"</string>
- <string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"Dozvoljava aplikaciji da čita istoriju svih URL adresa koje su posećene pomoću Pregledača, kao i sve obeleživače u Pregledaču. Napomena: Ova dozvola se možda na primenjuje na pregledače treće strane i druge aplikacije sa mogućnošću veb pregledanja."</string>
- <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"pisanje veb obeleživača i istorije"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"Dozvoljava aplikaciji da menja istoriju Pregledača ili obeleživače uskladištene na tabletu. Ovo može da omogući aplikaciji da briše ili menja podatke Pregledača. Napomena: Ova dozvola se možda na primenjuje na pregledače treće strane i druge aplikacije sa mogućnošću veb pregledanja."</string>
- <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Dozvoljava aplikaciji da menja istoriju pregledača ili obeleživače koji se čuvaju na Android TV uređaju. Ovo može da omogući aplikaciji da briše ili menja podatke pregledača. Napomena: ova dozvola se možda ne primenjuje na pregledače treće strane ni druge aplikacije sa mogućnostima veb-pregledanja."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"Dozvoljava aplikaciji da menja istoriju Pregledača ili obeleživače uskladištene na telefonu. Ovo može da omogući aplikaciji da briše ili menja podatke Pregledača. Napomena: Ova dozvola se možda na primenjuje na pregledače treće strane i druge aplikacije sa mogućnošću veb pregledanja."</string>
- <string name="permlab_setAlarm" msgid="1158001610254173567">"podešavanje alarma"</string>
- <string name="permdesc_setAlarm" msgid="2185033720060109640">"Dozvoljava aplikaciji da podesi alarm u instaliranoj aplikaciji budilnika. Neke aplikacije budilnika možda ne primenjuju ovu funkciju."</string>
- <string name="permlab_addVoicemail" msgid="4770245808840814471">"dodavanje govorne pošte"</string>
- <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Dozvoljava aplikaciji da dodaje poruke u prijemno sanduče govorne pošte."</string>
- <string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"izmena dozvola za geografske lokacije Pregledača"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Dozvoljava aplikaciji da izmeni dozvole Pregledača za utvrđivanje geografske lokacije. Zlonamerne aplikacije to mogu da zloupotrebe i iskoriste za slanje informacija o lokaciji nasumičnim veb-sajtovima."</string>
- <string name="save_password_message" msgid="2146409467245462965">"Želite li da pregledač zapamti ovu lozinku?"</string>
- <string name="save_password_notnow" msgid="2878327088951240061">"Ne sada"</string>
- <string name="save_password_remember" msgid="6490888932657708341">"Zapamti"</string>
- <string name="save_password_never" msgid="6776808375903410659">"Nikad"</string>
- <string name="open_permission_deny" msgid="5136793905306987251">"Nemate dozvolu da otvorite ovu stranicu."</string>
- <string name="text_copied" msgid="2531420577879738860">"Tekst je kopiran u privremenu memoriju."</string>
- <string name="pasted_from_app" msgid="5627698450808256545">"Aplikacija<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepila podatke iz aplikacije <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> je prelepio/la iz privremene memorije"</string>
- <string name="pasted_text" msgid="4298871641549173733">"Aplikacija<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepila tekst koji ste kopirali"</string>
- <string name="pasted_image" msgid="4729097394781491022">"Aplikacija<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepila sliku koju ste kopirali"</string>
- <string name="pasted_content" msgid="646276353060777131">"Aplikacija<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepila sadržaj koji ste kopirali"</string>
- <string name="more_item_label" msgid="7419249600215749115">"Još"</string>
- <string name="prepend_shortcut_label" msgid="1743716737502867951">"Meni+"</string>
+ <string name="autofill_province" msgid="3676846437741893159">"Покрајина"</string>
+ <string name="autofill_postal_code" msgid="7034789388968295591">"Поштански број"</string>
+ <string name="autofill_state" msgid="3341725337190434069">"Држава"</string>
+ <string name="autofill_zip_code" msgid="1315503730274962450">"Поштански број"</string>
+ <string name="autofill_county" msgid="7781382735643492173">"Округ"</string>
+ <string name="autofill_island" msgid="5367139008536593734">"Острво"</string>
+ <string name="autofill_district" msgid="6428712062213557327">"Дистрикт"</string>
+ <string name="autofill_department" msgid="9047276226873531529">"Одељење"</string>
+ <string name="autofill_prefecture" msgid="7267397763720241872">"Префектура"</string>
+ <string name="autofill_parish" msgid="6847960518334530198">"Парохија"</string>
+ <string name="autofill_area" msgid="8289022370678448983">"Област"</string>
+ <string name="autofill_emirate" msgid="2544082046790551168">"Емират"</string>
+ <string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"читање веб обележивача и историје"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"Дозвољава апликацији да чита историју свих URL адреса које су посећене помоћу Прегледача, као и све обележиваче у Прегледачу. Напомена: Ова дозвола се можда на примењује на прегледаче треће стране и друге апликације са могућношћу веб прегледања."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"писање веб обележивача и историје"</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"Дозвољава апликацији да мења историју Прегледача или обележиваче ускладиштене на таблету. Ово може да омогући апликацији да брише или мења податке Прегледача. Напомена: Ова дозвола се можда на примењује на прегледаче треће стране и друге апликације са могућношћу веб прегледања."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Дозвољава апликацији да мења историју прегледача или обележиваче који се чувају на Android TV уређају. Ово може да омогући апликацији да брише или мења податке прегледача. Напомена: ова дозвола се можда не примењује на прегледаче треће стране ни друге апликације са могућностима веб-прегледања."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"Дозвољава апликацији да мења историју Прегледача или обележиваче ускладиштене на телефону. Ово може да омогући апликацији да брише или мења податке Прегледача. Напомена: Ова дозвола се можда на примењује на прегледаче треће стране и друге апликације са могућношћу веб прегледања."</string>
+ <string name="permlab_setAlarm" msgid="1158001610254173567">"подешавање аларма"</string>
+ <string name="permdesc_setAlarm" msgid="2185033720060109640">"Дозвољава апликацији да подеси аларм у инсталираној апликацији будилника. Неке апликације будилника можда не примењују ову функцију."</string>
+ <string name="permlab_addVoicemail" msgid="4770245808840814471">"додавање говорне поште"</string>
+ <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Дозвољава апликацији да додаје поруке у пријемно сандуче говорне поште."</string>
+ <string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"измена дозвола за географске локације Прегледача"</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Дозвољава апликацији да измени дозволе Прегледача за утврђивање географске локације. Злонамерне апликације то могу да злоупотребе и искористе за слање информација о локацији насумичним веб-сајтовима."</string>
+ <string name="save_password_message" msgid="2146409467245462965">"Желите ли да прегледач запамти ову лозинку?"</string>
+ <string name="save_password_notnow" msgid="2878327088951240061">"Не сада"</string>
+ <string name="save_password_remember" msgid="6490888932657708341">"Запамти"</string>
+ <string name="save_password_never" msgid="6776808375903410659">"Никад"</string>
+ <string name="open_permission_deny" msgid="5136793905306987251">"Немате дозволу да отворите ову страницу."</string>
+ <string name="text_copied" msgid="2531420577879738860">"Текст је копиран у привремену меморију."</string>
+ <string name="pasted_from_app" msgid="5627698450808256545">"Апликација<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је налепила податке из апликације <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> је прелепио/ла из привремене меморије"</string>
+ <string name="pasted_text" msgid="4298871641549173733">"Апликација<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је налепила текст који сте копирали"</string>
+ <string name="pasted_image" msgid="4729097394781491022">"Апликација<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је налепила слику коју сте копирали"</string>
+ <string name="pasted_content" msgid="646276353060777131">"Апликација<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је налепила садржај који сте копирали"</string>
+ <string name="more_item_label" msgid="7419249600215749115">"Још"</string>
+ <string name="prepend_shortcut_label" msgid="1743716737502867951">"Мени+"</string>
<string name="menu_meta_shortcut_label" msgid="1623390163674762478">"Meta+"</string>
<string name="menu_ctrl_shortcut_label" msgid="131911133027196485">"Ctrl+"</string>
<string name="menu_alt_shortcut_label" msgid="343761069945250991">"Alt+"</string>
<string name="menu_shift_shortcut_label" msgid="5443936876111232346">"Shift+"</string>
<string name="menu_sym_shortcut_label" msgid="4037566049061218776">"Sym+"</string>
<string name="menu_function_shortcut_label" msgid="2367112760987662566">"Function+"</string>
- <string name="menu_space_shortcut_label" msgid="5949311515646872071">"razmak"</string>
+ <string name="menu_space_shortcut_label" msgid="5949311515646872071">"размак"</string>
<string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string>
- <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"izbriši"</string>
- <string name="search_go" msgid="2141477624421347086">"Pretraži"</string>
- <string name="search_hint" msgid="455364685740251925">"Pretražite…"</string>
- <string name="searchview_description_search" msgid="1045552007537359343">"Pretraži"</string>
- <string name="searchview_description_query" msgid="7430242366971716338">"Upit za pretragu"</string>
- <string name="searchview_description_clear" msgid="1989371719192982900">"Obriši upit"</string>
- <string name="searchview_description_submit" msgid="6771060386117334686">"Pošalji upit"</string>
- <string name="searchview_description_voice" msgid="42360159504884679">"Glasovna pretraga"</string>
- <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Omogućiti Istraživanje dodirom?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> želi da omogući Istraživanje dodirom. Kada je Istraživanje dodirom uključeno, možete da čujete ili vidite opise stavke na koju ste stavili prst ili da komunicirate sa tabletom pomoću pokreta."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> želi da omogući Istraživanje dodirom. Kada je Istraživanje dodirom uključeno, možete da čujete ili vidite opise stavke na koju ste stavili prst ili da komunicirate sa telefonom pomoću pokreta."</string>
- <string name="oneMonthDurationPast" msgid="4538030857114635777">"Pre mesec dana"</string>
- <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Pre mesec dana"</string>
- <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Poslednji # dan}one{Poslednji # dan}few{Poslednja # dana}other{Poslednjih # dana}}"</string>
- <string name="last_month" msgid="1528906781083518683">"Prošlog meseca"</string>
- <string name="older" msgid="1645159827884647400">"Starije"</string>
+ <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"избриши"</string>
+ <string name="search_go" msgid="2141477624421347086">"Претражи"</string>
+ <string name="search_hint" msgid="455364685740251925">"Претражите…"</string>
+ <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_voice" msgid="42360159504884679">"Гласовна претрага"</string>
+ <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Oмогућити Истраживање додиром?"</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> жели да омогући Истраживање додиром. Када је Истраживање додиром укључено, можете да чујете или видите описе ставке на коју сте ставили прст или да комуницирате са таблетом помоћу покрета."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> жели да омогући Истраживање додиром. Када је Истраживање додиром укључено, можете да чујете или видите описе ставке на коју сте ставили прст или да комуницирате са телефоном помоћу покрета."</string>
+ <string name="oneMonthDurationPast" msgid="4538030857114635777">"Пре месец дана"</string>
+ <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Пре месец дана"</string>
+ <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Последњи # дан}one{Последњи # дан}few{Последња # дана}other{Последњих # дана}}"</string>
+ <string name="last_month" msgid="1528906781083518683">"Прошлог месеца"</string>
+ <string name="older" msgid="1645159827884647400">"Старије"</string>
<string name="preposition_for_date" msgid="2780767868832729599">"<xliff:g id="DATE">%s</xliff:g>"</string>
- <string name="preposition_for_time" msgid="4336835286453822053">"u <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="preposition_for_year" msgid="3149809685340130039">"u <xliff:g id="YEAR">%s</xliff:g>."</string>
- <string name="day" msgid="8394717255950176156">"dan"</string>
- <string name="days" msgid="4570879797423034973">"dana"</string>
- <string name="hour" msgid="7796325297097314653">"sat"</string>
- <string name="hours" msgid="8517014849629200683">"sata"</string>
- <string name="minute" msgid="8369209540986467610">"min"</string>
- <string name="minutes" msgid="3456532942641808971">"min"</string>
- <string name="second" msgid="9210875257112211713">"sek"</string>
- <string name="seconds" msgid="2175052687727971048">"sek"</string>
- <string name="week" msgid="907127093960923779">"nedelja"</string>
- <string name="weeks" msgid="3516247214269821391">"nedelje/a"</string>
- <string name="year" msgid="5182610307741238982">"godina"</string>
- <string name="years" msgid="5797714729103773425">"godine(a)"</string>
- <string name="now_string_shortest" msgid="3684914126941650330">"sada"</string>
- <string name="duration_minutes_shortest" msgid="5744379079540806690">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
- <string name="duration_hours_shortest" msgid="1477752094141971675">"<xliff:g id="COUNT">%d</xliff:g> s"</string>
- <string name="duration_days_shortest" msgid="4083124701676227233">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
- <string name="duration_years_shortest" msgid="483982719231145618">"<xliff:g id="COUNT">%d</xliff:g> god"</string>
- <string name="duration_minutes_shortest_future" msgid="5260857299282734759">"za <xliff:g id="COUNT">%d</xliff:g> min"</string>
- <string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> s"</string>
- <string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
- <string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> god"</string>
- <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pre # minut}one{Pre # minut}few{Pre # minuta}other{Pre # minuta}}"</string>
- <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pre # sat}one{Pre # sat}few{Pre # sata}other{Pre # sati}}"</string>
- <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pre # dan}one{Pre # dan}few{Pre # dana}other{Pre # dana}}"</string>
- <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Pre # godinu}one{Pre # godinu}few{Pre # godine}other{Pre # godina}}"</string>
- <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minut}one{# minut}few{# minuta}other{# minuta}}"</string>
- <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# sat}one{# sat}few{# sata}other{# sati}}"</string>
- <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dan}one{# dan}few{# dana}other{# dana}}"</string>
- <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# godina}one{# godina}few{# godine}other{# godina}}"</string>
- <string name="VideoView_error_title" msgid="5750686717225068016">"Problem sa video snimkom"</string>
- <string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Ovaj video ne može da se strimuje na ovom uređaju."</string>
- <string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Ne možete da pustite ovaj video."</string>
- <string name="VideoView_error_button" msgid="5138809446603764272">"Potvrdi"</string>
+ <string name="preposition_for_time" msgid="4336835286453822053">"у <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="preposition_for_year" msgid="3149809685340130039">"у <xliff:g id="YEAR">%s</xliff:g>."</string>
+ <string name="day" msgid="8394717255950176156">"дан"</string>
+ <string name="days" msgid="4570879797423034973">"дана"</string>
+ <string name="hour" msgid="7796325297097314653">"сат"</string>
+ <string name="hours" msgid="8517014849629200683">"сата"</string>
+ <string name="minute" msgid="8369209540986467610">"мин"</string>
+ <string name="minutes" msgid="3456532942641808971">"мин"</string>
+ <string name="second" msgid="9210875257112211713">"сек"</string>
+ <string name="seconds" msgid="2175052687727971048">"сек"</string>
+ <string name="week" msgid="907127093960923779">"недеља"</string>
+ <string name="weeks" msgid="3516247214269821391">"недеље/а"</string>
+ <string name="year" msgid="5182610307741238982">"година"</string>
+ <string name="years" msgid="5797714729103773425">"годинe(а)"</string>
+ <string name="now_string_shortest" msgid="3684914126941650330">"сада"</string>
+ <string name="duration_minutes_shortest" msgid="5744379079540806690">"<xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_shortest" msgid="1477752094141971675">"<xliff:g id="COUNT">%d</xliff:g> с"</string>
+ <string name="duration_days_shortest" msgid="4083124701676227233">"<xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_shortest" msgid="483982719231145618">"<xliff:g id="COUNT">%d</xliff:g> год"</string>
+ <string name="duration_minutes_shortest_future" msgid="5260857299282734759">"за <xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_shortest_future" msgid="2979276794547981674">"за <xliff:g id="COUNT">%d</xliff:g> с"</string>
+ <string name="duration_days_shortest_future" msgid="3392722163935571543">"за <xliff:g id="COUNT">%d</xliff:g> д"</string>
+ <string name="duration_years_shortest_future" msgid="5537464088352970388">"за <xliff:g id="COUNT">%d</xliff:g> год"</string>
+ <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Пре # минут}one{Пре # минут}few{Пре # минута}other{Пре # минута}}"</string>
+ <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Пре # сат}one{Пре # сат}few{Пре # сата}other{Пре # сати}}"</string>
+ <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Пре # дан}one{Пре # дан}few{Пре # дана}other{Пре # дана}}"</string>
+ <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Пре # годину}one{Пре # годину}few{Пре # године}other{Пре # година}}"</string>
+ <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# минут}one{# минут}few{# минута}other{# минута}}"</string>
+ <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# сат}one{# сат}few{# сата}other{# сати}}"</string>
+ <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# дан}one{# дан}few{# дана}other{# дана}}"</string>
+ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# година}one{# година}few{# године}other{# година}}"</string>
+ <string name="VideoView_error_title" msgid="5750686717225068016">"Проблем са видео снимком"</string>
+ <string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Овај видео не може да се стримује на овом уређају."</string>
+ <string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Не можете да пустите овај видео."</string>
+ <string name="VideoView_error_button" msgid="5138809446603764272">"Потврди"</string>
<string name="relative_time" msgid="8572030016028033243">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="noon" msgid="8365974533050605886">"podne"</string>
- <string name="Noon" msgid="6902418443846838189">"Podne"</string>
- <string name="midnight" msgid="3646671134282785114">"ponoć"</string>
- <string name="Midnight" msgid="8176019203622191377">"Ponoć"</string>
+ <string name="noon" msgid="8365974533050605886">"подне"</string>
+ <string name="Noon" msgid="6902418443846838189">"Подне"</string>
+ <string name="midnight" msgid="3646671134282785114">"поноћ"</string>
+ <string name="Midnight" msgid="8176019203622191377">"Поноћ"</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">"Izaberi sve"</string>
- <string name="cut" msgid="2561199725874745819">"Iseci"</string>
- <string name="copy" msgid="5472512047143665218">"Kopiraj"</string>
- <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Kopiranje u privremenu memoriju nije uspelo"</string>
- <string name="paste" msgid="461843306215520225">"Nalepi"</string>
- <string name="paste_as_plain_text" msgid="7664800665823182587">"Nalepi kao običan tekst"</string>
- <string name="replace" msgid="7842675434546657444">"Zameni..."</string>
- <string name="delete" msgid="1514113991712129054">"Izbriši"</string>
- <string name="copyUrl" msgid="6229645005987260230">"Kopiraj URL adresu"</string>
- <string name="selectTextMode" msgid="3225108910999318778">"Izaberi tekst"</string>
- <string name="undo" msgid="3175318090002654673">"Opozovi"</string>
- <string name="redo" msgid="7231448494008532233">"Ponovi"</string>
- <string name="autofill" msgid="511224882647795296">"Automatsko popunjavanje"</string>
- <string name="textSelectionCABTitle" msgid="5151441579532476940">"Izbor teksta"</string>
- <string name="addToDictionary" msgid="8041821113480950096">"Dodaj u rečnik"</string>
- <string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
- <string name="inputMethod" msgid="1784759500516314751">"Metod unosa"</string>
- <string name="editTextMenuTitle" msgid="857666911134482176">"Radnje u vezi sa tekstom"</string>
- <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Nazad"</string>
- <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Promenite metod unosa"</string>
- <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Memorijski prostor je na izmaku"</string>
- <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke sistemske funkcije možda ne funkcionišu"</string>
- <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."</string>
- <string name="app_running_notification_title" msgid="8985999749231486569">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je pokrenuta"</string>
- <string name="app_running_notification_text" msgid="5120815883400228566">"Dodirnite za više informacija ili zaustavljanje aplikacije."</string>
- <string name="ok" msgid="2646370155170753815">"Potvrdi"</string>
- <string name="cancel" msgid="6908697720451760115">"Otkaži"</string>
- <string name="yes" msgid="9069828999585032361">"Potvrdi"</string>
- <string name="no" msgid="5122037903299899715">"Otkaži"</string>
- <string name="dialog_alert_title" msgid="651856561974090712">"Pažnja"</string>
- <string name="loading" msgid="3138021523725055037">"Učitava se…"</string>
- <string name="capital_on" msgid="2770685323900821829">"DA"</string>
- <string name="capital_off" msgid="7443704171014626777">"NE"</string>
- <string name="checked" msgid="9179896827054513119">"označeno je"</string>
- <string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
- <string name="selected" msgid="6614607926197755875">"izabrano"</string>
- <string name="not_selected" msgid="410652016565864475">"nije izabrano"</string>
- <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Jedna zvezdica od {max}}one{# zvezdica od {max}}few{# zvezdice od {max}}other{# zvezdica od {max}}}"</string>
- <string name="in_progress" msgid="2149208189184319441">"u toku"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
- <string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
- <string name="whichViewApplication" msgid="5733194231473132945">"Otvorite pomoću"</string>
- <string name="whichViewApplicationNamed" msgid="415164730629690105">"Otvorite pomoću aplikacije %1$s"</string>
- <string name="whichViewApplicationLabel" msgid="7367556735684742409">"Otvori"</string>
- <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Otvarajte <xliff:g id="HOST">%1$s</xliff:g> linkove pomoću"</string>
- <string name="whichOpenLinksWith" msgid="1120936181362907258">"Otvaraj linkove pomoću"</string>
- <string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Otvarajte linkove pomoću aplikacije <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
- <string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Otvarajte <xliff:g id="HOST">%1$s</xliff:g> linkove pomoću aplikacije <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
- <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Dozvoli pristup"</string>
- <string name="whichEditApplication" msgid="6191568491456092812">"Izmenite pomoću"</string>
- <string name="whichEditApplicationNamed" msgid="8096494987978521514">"Izmenite pomoću aplikacije %1$s"</string>
- <string name="whichEditApplicationLabel" msgid="1463288652070140285">"Izmeni"</string>
- <string name="whichSendApplication" msgid="4143847974460792029">"Delite"</string>
- <string name="whichSendApplicationNamed" msgid="4470386782693183461">"Delite pomoću aplikacije %1$s"</string>
- <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Deli"</string>
- <string name="whichSendToApplication" msgid="77101541959464018">"Pošaljite pomoću:"</string>
- <string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Pošaljite pomoću: %1$s"</string>
- <string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Pošalji"</string>
- <string name="whichHomeApplication" msgid="8276350727038396616">"Izaberite aplikaciju za početnu stranicu"</string>
- <string name="whichHomeApplicationNamed" msgid="5855990024847433794">"Koristite %1$s za početnu"</string>
- <string name="whichHomeApplicationLabel" msgid="8907334282202933959">"Snimite sliku"</string>
- <string name="whichImageCaptureApplication" msgid="2737413019463215284">"Snimite sliku pomoću aplikacije"</string>
- <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Snimite sliku pomoću aplikacije %1$s"</string>
- <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Snimite sliku"</string>
- <string name="alwaysUse" msgid="3153558199076112903">"Podrazumevano koristi za ovu radnju."</string>
- <string name="use_a_different_app" msgid="4987790276170972776">"Koristite drugu aplikaciju"</string>
- <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Obrišite podrazumevano podešavanje u meniju Podešavanja sistema &gt; Aplikacije &gt; Preuzeto."</string>
- <string name="chooseActivity" msgid="8563390197659779956">"Izaberite radnju"</string>
- <string name="chooseUsbActivity" msgid="2096269989990986612">"Izbor aplikacije za USB uređaj"</string>
- <string name="noApplications" msgid="1186909265235544019">"Nijedna aplikacija ne može da obavlja ovu radnju."</string>
- <string name="aerr_application" msgid="4090916809370389109">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> je zaustavljena"</string>
- <string name="aerr_process" msgid="4268018696970966407">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> je zaustavljen"</string>
- <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> se stalno zaustavlja"</string>
- <string name="aerr_process_repeated" msgid="1153152413537954974">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> se stalno zaustavlja"</string>
- <string name="aerr_restart" msgid="2789618625210505419">"Ponovo otvori aplikaciju"</string>
- <string name="aerr_report" msgid="3095644466849299308">"Pošaljite povratne informacije"</string>
- <string name="aerr_close" msgid="3398336821267021852">"Zatvori"</string>
- <string name="aerr_mute" msgid="2304972923480211376">"Ignoriši dok se uređaj ne pokrene ponovo"</string>
- <string name="aerr_wait" msgid="3198677780474548217">"Čekaj"</string>
- <string name="aerr_close_app" msgid="8318883106083050970">"Zatvori aplikaciju"</string>
+ <string name="selectAll" msgid="1532369154488982046">"Изабери све"</string>
+ <string name="cut" msgid="2561199725874745819">"Исеци"</string>
+ <string name="copy" msgid="5472512047143665218">"Копирај"</string>
+ <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Копирање у привремену меморију није успело"</string>
+ <string name="paste" msgid="461843306215520225">"Налепи"</string>
+ <string name="paste_as_plain_text" msgid="7664800665823182587">"Налепи као обичан текст"</string>
+ <string name="replace" msgid="7842675434546657444">"Замени..."</string>
+ <string name="delete" msgid="1514113991712129054">"Избриши"</string>
+ <string name="copyUrl" msgid="6229645005987260230">"Копирај URL адресу"</string>
+ <string name="selectTextMode" msgid="3225108910999318778">"Изабери текст"</string>
+ <string name="undo" msgid="3175318090002654673">"Опозови"</string>
+ <string name="redo" msgid="7231448494008532233">"Понови"</string>
+ <string name="autofill" msgid="511224882647795296">"Аутоматско попуњавање"</string>
+ <string name="textSelectionCABTitle" msgid="5151441579532476940">"Избор текста"</string>
+ <string name="addToDictionary" msgid="8041821113480950096">"Додај у речник"</string>
+ <string name="deleteText" msgid="4200807474529938112">"Избриши"</string>
+ <string name="inputMethod" msgid="1784759500516314751">"Метод уноса"</string>
+ <string name="editTextMenuTitle" msgid="857666911134482176">"Радње у вези са текстом"</string>
+ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string>
+ <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Промените метод уноса"</string>
+ <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Меморијски простор је на измаку"</string>
+ <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Неке системске функције можда не функционишу"</string>
+ <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."</string>
+ <string name="app_running_notification_title" msgid="8985999749231486569">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је покренута"</string>
+ <string name="app_running_notification_text" msgid="5120815883400228566">"Додирните за више информација или заустављање апликације."</string>
+ <string name="ok" msgid="2646370155170753815">"Потврди"</string>
+ <string name="cancel" msgid="6908697720451760115">"Откажи"</string>
+ <string name="yes" msgid="9069828999585032361">"Потврди"</string>
+ <string name="no" msgid="5122037903299899715">"Откажи"</string>
+ <string name="dialog_alert_title" msgid="651856561974090712">"Пажња"</string>
+ <string name="loading" msgid="3138021523725055037">"Учитава се…"</string>
+ <string name="capital_on" msgid="2770685323900821829">"ДА"</string>
+ <string name="capital_off" msgid="7443704171014626777">"НЕ"</string>
+ <string name="checked" msgid="9179896827054513119">"означено је"</string>
+ <string name="not_checked" msgid="7972320087569023342">"није означено"</string>
+ <string name="selected" msgid="6614607926197755875">"изабрано"</string>
+ <string name="not_selected" msgid="410652016565864475">"није изабрано"</string>
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Једна звездица од {max}}one{# звездица од {max}}few{# звездице од {max}}other{# звездица од {max}}}"</string>
+ <string name="in_progress" msgid="2149208189184319441">"у току"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
+ <string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
+ <string name="whichViewApplication" msgid="5733194231473132945">"Отворите помоћу"</string>
+ <string name="whichViewApplicationNamed" msgid="415164730629690105">"Отворите помоћу апликације %1$s"</string>
+ <string name="whichViewApplicationLabel" msgid="7367556735684742409">"Отвори"</string>
+ <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Отварајте <xliff:g id="HOST">%1$s</xliff:g> линкове помоћу"</string>
+ <string name="whichOpenLinksWith" msgid="1120936181362907258">"Отварај линкове помоћу"</string>
+ <string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Отварајте линкове помоћу апликације <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
+ <string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Отварајте <xliff:g id="HOST">%1$s</xliff:g> линкове помоћу апликације <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Дозволи приступ"</string>
+ <string name="whichEditApplication" msgid="6191568491456092812">"Измените помоћу"</string>
+ <string name="whichEditApplicationNamed" msgid="8096494987978521514">"Измените помоћу апликације %1$s"</string>
+ <string name="whichEditApplicationLabel" msgid="1463288652070140285">"Измени"</string>
+ <string name="whichSendApplication" msgid="4143847974460792029">"Делите"</string>
+ <string name="whichSendApplicationNamed" msgid="4470386782693183461">"Делите помоћу апликације %1$s"</string>
+ <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Дели"</string>
+ <string name="whichSendToApplication" msgid="77101541959464018">"Пошаљите помоћу:"</string>
+ <string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Пошаљите помоћу: %1$s"</string>
+ <string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Пошаљи"</string>
+ <string name="whichHomeApplication" msgid="8276350727038396616">"Изаберите апликацију за почетну страницу"</string>
+ <string name="whichHomeApplicationNamed" msgid="5855990024847433794">"Користите %1$s за почетну"</string>
+ <string name="whichHomeApplicationLabel" msgid="8907334282202933959">"Снимите слику"</string>
+ <string name="whichImageCaptureApplication" msgid="2737413019463215284">"Снимите слику помоћу апликације"</string>
+ <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Снимите слику помоћу апликације %1$s"</string>
+ <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Снимите слику"</string>
+ <string name="alwaysUse" msgid="3153558199076112903">"Подразумевано користи за ову радњу."</string>
+ <string name="use_a_different_app" msgid="4987790276170972776">"Користите другу апликацију"</string>
+ <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Обришите подразумевано подешавање у менију Подешавања система &gt; Апликације &gt; Преузето."</string>
+ <string name="chooseActivity" msgid="8563390197659779956">"Изаберите радњу"</string>
+ <string name="chooseUsbActivity" msgid="2096269989990986612">"Избор апликације за USB уређај"</string>
+ <string name="noApplications" msgid="1186909265235544019">"Ниједна апликација не може да обавља ову радњу."</string>
+ <string name="aerr_application" msgid="4090916809370389109">"Апликација <xliff:g id="APPLICATION">%1$s</xliff:g> је заустављена"</string>
+ <string name="aerr_process" msgid="4268018696970966407">"Процес <xliff:g id="PROCESS">%1$s</xliff:g> је заустављен"</string>
+ <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> се стално зауставља"</string>
+ <string name="aerr_process_repeated" msgid="1153152413537954974">"Процес <xliff:g id="PROCESS">%1$s</xliff:g> се стално зауставља"</string>
+ <string name="aerr_restart" msgid="2789618625210505419">"Поново отвори апликацију"</string>
+ <string name="aerr_report" msgid="3095644466849299308">"Пошаљите повратне информације"</string>
+ <string name="aerr_close" msgid="3398336821267021852">"Затвори"</string>
+ <string name="aerr_mute" msgid="2304972923480211376">"Игнориши док се уређај не покрене поново"</string>
+ <string name="aerr_wait" msgid="3198677780474548217">"Чекај"</string>
+ <string name="aerr_close_app" msgid="8318883106083050970">"Затвори апликацију"</string>
<string name="anr_title" msgid="7290329487067300120"></string>
- <string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g> ne reaguje"</string>
- <string name="anr_activity_process" msgid="3477362583767128667">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ne reaguje"</string>
- <string name="anr_application_process" msgid="4978772139461676184">"<xliff:g id="APPLICATION">%1$s</xliff:g> ne reaguje"</string>
- <string name="anr_process" msgid="1664277165911816067">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> ne reaguje"</string>
- <string name="force_close" msgid="9035203496368973803">"Potvrdi"</string>
- <string name="report" msgid="2149194372340349521">"Prijavi"</string>
- <string name="wait" msgid="7765985809494033348">"Sačekaj"</string>
- <string name="webpage_unresponsive" msgid="7850879412195273433">"Stranica je prestala da se odaziva.\n\n Da li želite da je zatvorite?"</string>
- <string name="launch_warning_title" msgid="6725456009564953595">"Aplikacija je preusmerena"</string>
- <string name="launch_warning_replace" msgid="3073392976283203402">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je sada pokrenuta."</string>
- <string name="launch_warning_original" msgid="3332206576800169626">"Prvobitno je pokrenuta aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
- <string name="screen_compat_mode_scale" msgid="8627359598437527726">"Razmera"</string>
- <string name="screen_compat_mode_show" msgid="5080361367584709857">"Uvek prikazuj"</string>
- <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Ponovo omogućite u meniju Sistemska podešavanja &gt; Aplikacije &gt; Preuzeto."</string>
- <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava trenutno podešavanje veličine prikaza i može da se ponaša neočekivano."</string>
- <string name="unsupported_display_size_show" msgid="980129850974919375">"Uvek prikazuj"</string>
- <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je napravljena za nekompatibilnu verziju Android OS-a i može da se ponaša na neočekivan način. Možda je dostupna ažurirana verzija aplikacije."</string>
- <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Uvek prikaži"</string>
- <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Potraži ažuriranje"</string>
- <string name="smv_application" msgid="3775183542777792638">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) je prekršila samonametnute StrictMode smernice."</string>
- <string name="smv_process" msgid="1398801497130695446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> je prekršio samonametnute StrictMode smernice."</string>
- <string name="android_upgrading_title" product="default" msgid="7279077384220829683">"Telefon se ažurira…"</string>
- <string name="android_upgrading_title" product="tablet" msgid="4268417249079938805">"Tablet se ažurira…"</string>
- <string name="android_upgrading_title" product="device" msgid="6774767702998149762">"Uređaj se ažurira…"</string>
- <string name="android_start_title" product="default" msgid="4036708252778757652">"Telefon se pokreće…"</string>
- <string name="android_start_title" product="automotive" msgid="7917984412828168079">"Android se pokreće…"</string>
- <string name="android_start_title" product="tablet" msgid="4429767260263190344">"Tablet se pokreće…"</string>
- <string name="android_start_title" product="device" msgid="6967413819673299309">"Uređaj se pokreće…"</string>
- <string name="android_upgrading_fstrim" msgid="3259087575528515329">"Memorija se optimizuje."</string>
- <string name="android_upgrading_notification_title" product="default" msgid="3509927005342279257">"Ažuriranje sistema se dovršava…"</string>
- <string name="app_upgrading_toast" msgid="1016267296049455585">"<xliff:g id="APPLICATION">%1$s</xliff:g> se nadograđuje…"</string>
- <string name="android_upgrading_apk" msgid="1339564803894466737">"Optimizovanje aplikacije <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
- <string name="android_preparing_apk" msgid="589736917792300956">"Priprema se <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
- <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="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>
- <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Nastavi"</string>
- <string name="heavy_weight_notification" msgid="8382784283600329576">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> je pokrenuta"</string>
- <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dodirnite da biste se vratili u igru"</string>
- <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Odaberite igru"</string>
- <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"Da bi performanse bile bolje, može da bude otvorena samo jedna od ovih igara."</string>
- <string name="old_app_action" msgid="725331621042848590">"Nazad na <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
- <string name="new_app_action" msgid="547772182913269801">"Otvori <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> će se zatvoriti bez čuvanja"</string>
- <string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> premašuje ograničenje memorije"</string>
- <string name="dump_heap_ready_notification" msgid="2302452262927390268">"Snimak dinamičkog dela memorije za proces <xliff:g id="PROC">%1$s</xliff:g> je spreman"</string>
- <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Snimak dinamičkog dela memorije je napravljen. Dodirnite za deljenje."</string>
- <string name="dump_heap_title" msgid="4367128917229233901">"Želite li da delite snimak dinamičkog dela memorije?"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"Proces <xliff:g id="PROC">%1$s</xliff:g> je premašio ograničenje memorije od <xliff:g id="SIZE">%2$s</xliff:g>. Snimak dinamičkog dela memorije je dostupan i možete da ga delite sa programerom. Budite oprezni: ovaj snimak dinamičkog dela memorije može da sadrži neke lične podatke kojima aplikacija može da pristupa."</string>
- <string name="dump_heap_system_text" msgid="6805155514925350849">"Proces <xliff:g id="PROC">%1$s</xliff:g> je premašio ograničenje memorije od <xliff:g id="SIZE">%2$s</xliff:g>. Snimak dinamičkog dela memorije je dostupan i možete da ga delite. Budite oprezni: ovaj snimak dinamičkog dela memorije može da sadrži osetljive lične podatke kojima proces može da pristupa, što može da obuhvata tekst koji ste unosili."</string>
- <string name="dump_heap_ready_text" msgid="5849618132123045516">"Snimak dinamičkog dela memorije za proces <xliff:g id="PROC">%1$s</xliff:g> je dostupan i možete da ga delite. Budite oprezni: ovaj snimak dinamičkog dela memorije može da sadrži osetljive lične podatke kojima proces može da pristupa, što može da obuhvata tekst koji ste unosili."</string>
- <string name="sendText" msgid="493003724401350724">"Izaberite radnju za tekst"</string>
- <string name="volume_ringtone" msgid="134784084629229029">"Jačina zvuka zvona"</string>
- <string name="volume_music" msgid="7727274216734955095">"Jačina zvuka medija"</string>
- <string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"Igranje preko Bluetooth-a"</string>
- <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"Podešen je nečujni zvuk zvona"</string>
- <string name="volume_call" msgid="7625321655265747433">"Jačina zvuka dolaznog poziva"</string>
- <string name="volume_bluetooth_call" msgid="2930204618610115061">"Jačina zvuka dolazećeg poziva preko Bluetooth-a"</string>
- <string name="volume_alarm" msgid="4486241060751798448">"Jačina zvuka alarma"</string>
- <string name="volume_notification" msgid="6864412249031660057">"Jačina zvuka za obaveštenja"</string>
- <string name="volume_unknown" msgid="4041914008166576293">"Jačina zvuka"</string>
- <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"Jačina zvuka Bluetooth uređaja"</string>
- <string name="volume_icon_description_ringer" msgid="2187800636867423459">"Jačina melodije zvona"</string>
- <string name="volume_icon_description_incall" msgid="4491255105381227919">"Jačina zvuka poziva"</string>
- <string name="volume_icon_description_media" msgid="4997633254078171233">"Jačina zvuka medija"</string>
- <string name="volume_icon_description_notification" msgid="579091344110747279">"Jačina zvuka obaveštenja"</string>
- <string name="ringtone_default" msgid="9118299121288174597">"Podrazumevani zvuk zvona"</string>
- <string name="ringtone_default_with_actual" msgid="2709686194556159773">"Podrazumevano (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
- <string name="ringtone_silent" msgid="397111123930141876">"Bez zvuka"</string>
- <string name="ringtone_picker_title" msgid="667342618626068253">"Zvukovi zvona"</string>
- <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Zvuci alarma"</string>
- <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Zvuci obaveštenja"</string>
- <string name="ringtone_unknown" msgid="5059495249862816475">"Nepoznato"</string>
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavljivanje na WiFi mrežu"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prijavite se na mrežu"</string>
+ <string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g> не реагује"</string>
+ <string name="anr_activity_process" msgid="3477362583767128667">"<xliff:g id="ACTIVITY">%1$s</xliff:g> не реагује"</string>
+ <string name="anr_application_process" msgid="4978772139461676184">"<xliff:g id="APPLICATION">%1$s</xliff:g> не реагује"</string>
+ <string name="anr_process" msgid="1664277165911816067">"Процес <xliff:g id="PROCESS">%1$s</xliff:g> не реагује"</string>
+ <string name="force_close" msgid="9035203496368973803">"Потврди"</string>
+ <string name="report" msgid="2149194372340349521">"Пријави"</string>
+ <string name="wait" msgid="7765985809494033348">"Сачекај"</string>
+ <string name="webpage_unresponsive" msgid="7850879412195273433">"Страница је престала да се одазива.\n\n Да ли желите да је затворите?"</string>
+ <string name="launch_warning_title" msgid="6725456009564953595">"Апликација је преусмерена"</string>
+ <string name="launch_warning_replace" msgid="3073392976283203402">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је сада покренута."</string>
+ <string name="launch_warning_original" msgid="3332206576800169626">"Првобитно је покренута апликација <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+ <string name="screen_compat_mode_scale" msgid="8627359598437527726">"Размера"</string>
+ <string name="screen_compat_mode_show" msgid="5080361367584709857">"Увек приказуј"</string>
+ <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Поново омогућите у менију Системска подешавања &gt; Апликације &gt; Преузето."</string>
+ <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> не подржава тренутно подешавање величине приказа и може да се понаша неочекивано."</string>
+ <string name="unsupported_display_size_show" msgid="980129850974919375">"Увек приказуј"</string>
+ <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је направљена за некомпатибилну верзију Android ОС-а и може да се понаша на неочекиван начин. Можда је доступна ажурирана верзија апликације."</string>
+ <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Увек прикажи"</string>
+ <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Потражи ажурирање"</string>
+ <string name="smv_application" msgid="3775183542777792638">"Апликација <xliff:g id="APPLICATION">%1$s</xliff:g> (процес <xliff:g id="PROCESS">%2$s</xliff:g>) је прекршила самонаметнуте StrictMode смернице."</string>
+ <string name="smv_process" msgid="1398801497130695446">"Процес <xliff:g id="PROCESS">%1$s</xliff:g> је прекршио самонаметнуте StrictMode смернице."</string>
+ <string name="android_upgrading_title" product="default" msgid="7279077384220829683">"Телефон се ажурира…"</string>
+ <string name="android_upgrading_title" product="tablet" msgid="4268417249079938805">"Таблет се ажурира…"</string>
+ <string name="android_upgrading_title" product="device" msgid="6774767702998149762">"Уређај се ажурира…"</string>
+ <string name="android_start_title" product="default" msgid="4036708252778757652">"Телефон се покреће…"</string>
+ <string name="android_start_title" product="automotive" msgid="7917984412828168079">"Android се покреће…"</string>
+ <string name="android_start_title" product="tablet" msgid="4429767260263190344">"Таблет се покреће…"</string>
+ <string name="android_start_title" product="device" msgid="6967413819673299309">"Уређај се покреће…"</string>
+ <string name="android_upgrading_fstrim" msgid="3259087575528515329">"Меморија се оптимизује."</string>
+ <string name="android_upgrading_notification_title" product="default" msgid="3509927005342279257">"Ажурирање система се довршава…"</string>
+ <string name="app_upgrading_toast" msgid="1016267296049455585">"<xliff:g id="APPLICATION">%1$s</xliff:g> се надограђује…"</string>
+ <string name="android_upgrading_apk" msgid="1339564803894466737">"Оптимизовање апликације <xliff:g id="NUMBER_0">%1$d</xliff:g> од <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="android_preparing_apk" msgid="589736917792300956">"Припрема се <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
+ <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="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>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Настави"</string>
+ <string name="heavy_weight_notification" msgid="8382784283600329576">"Апликација <xliff:g id="APP">%1$s</xliff:g> је покренута"</string>
+ <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Додирните да бисте се вратили у игру"</string>
+ <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Одаберите игру"</string>
+ <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"Да би перформансе биле боље, може да буде отворена само једна од ових игара."</string>
+ <string name="old_app_action" msgid="725331621042848590">"Назад на <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
+ <string name="new_app_action" msgid="547772182913269801">"Отвори <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> ће се затворити без чувања"</string>
+ <string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> премашује ограничење меморије"</string>
+ <string name="dump_heap_ready_notification" msgid="2302452262927390268">"Снимак динамичког дела меморије за процес <xliff:g id="PROC">%1$s</xliff:g> је спреман"</string>
+ <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Снимак динамичког дела меморије је направљен. Додирните за дељење."</string>
+ <string name="dump_heap_title" msgid="4367128917229233901">"Желите ли да делите снимак динамичког дела меморије?"</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"Процес <xliff:g id="PROC">%1$s</xliff:g> је премашио ограничење меморије од <xliff:g id="SIZE">%2$s</xliff:g>. Снимак динамичког дела меморије је доступан и можете да га делите са програмером. Будите опрезни: овај снимак динамичког дела меморије може да садржи неке личне податке којима апликација може да приступа."</string>
+ <string name="dump_heap_system_text" msgid="6805155514925350849">"Процес <xliff:g id="PROC">%1$s</xliff:g> је премашио ограничење меморије од <xliff:g id="SIZE">%2$s</xliff:g>. Снимак динамичког дела меморије је доступан и можете да га делите. Будите опрезни: овај снимак динамичког дела меморије може да садржи осетљиве личне податке којима процес може да приступа, што може да обухвата текст који сте уносили."</string>
+ <string name="dump_heap_ready_text" msgid="5849618132123045516">"Снимак динамичког дела меморије за процес <xliff:g id="PROC">%1$s</xliff:g> је доступан и можете да га делите. Будите опрезни: овај снимак динамичког дела меморије може да садржи осетљиве личне податке којима процес може да приступа, што може да обухвата текст који сте уносили."</string>
+ <string name="sendText" msgid="493003724401350724">"Изаберите радњу за текст"</string>
+ <string name="volume_ringtone" msgid="134784084629229029">"Јачина звука звона"</string>
+ <string name="volume_music" msgid="7727274216734955095">"Јачина звука медија"</string>
+ <string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"Играње преко Bluetooth-а"</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">"Јачина звука долазећег позива преко Bluetooth-а"</string>
+ <string name="volume_alarm" msgid="4486241060751798448">"Јачина звука аларма"</string>
+ <string name="volume_notification" msgid="6864412249031660057">"Јачина звука за обавештења"</string>
+ <string name="volume_unknown" msgid="4041914008166576293">"Јачина звука"</string>
+ <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"Јачина звука Bluetooth уређаја"</string>
+ <string name="volume_icon_description_ringer" msgid="2187800636867423459">"Јачина мелодије звона"</string>
+ <string name="volume_icon_description_incall" msgid="4491255105381227919">"Јачина звука позива"</string>
+ <string name="volume_icon_description_media" msgid="4997633254078171233">"Јачина звука медија"</string>
+ <string name="volume_icon_description_notification" msgid="579091344110747279">"Јачина звука обавештења"</string>
+ <string name="ringtone_default" msgid="9118299121288174597">"Подразумевани звук звона"</string>
+ <string name="ringtone_default_with_actual" msgid="2709686194556159773">"Подразумевано (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+ <string name="ringtone_silent" msgid="397111123930141876">"Без звука"</string>
+ <string name="ringtone_picker_title" msgid="667342618626068253">"Звукови звона"</string>
+ <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Звуци аларма"</string>
+ <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Звуци обавештења"</string>
+ <string name="ringtone_unknown" msgid="5059495249862816475">"Непознато"</string>
+ <string name="wifi_available_sign_in" msgid="381054692557675237">"Пријављивање на WiFi мрежу"</string>
+ <string name="network_available_sign_in" msgid="1520342291829283114">"Пријавите се на мрежу"</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> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Pristup privatnom DNS serveru nije uspeo"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu vezu"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da biste se ipak povezali"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Prešli ste na tip mreže <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Uređaj koristi tip mreže <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kada tip mreže <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu. Možda će se naplaćivati troškovi."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Prešli ste sa tipa mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na tip mreže <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема приступ интернету"</string>
+ <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Додирните за опције"</string>
+ <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилна мрежа нема приступ интернету"</string>
+ <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежа нема приступ интернету"</string>
+ <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Приступ приватном DNS серверу није успео"</string>
+ <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничену везу"</string>
+ <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Додирните да бисте се ипак повезали"</string>
+ <string name="network_switch_metered" msgid="1531869544142283384">"Прешли сте на тип мреже <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1358296010128405906">"Уређај користи тип мреже <xliff:g id="NEW_NETWORK">%1$s</xliff:g> када тип мреже <xliff:g id="PREVIOUS_NETWORK">%2$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">"mobilni podaci"</item>
+ <item msgid="2255670471736226365">"мобилни подаци"</item>
<item msgid="5520925862115353992">"WiFi"</item>
<item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Eternet"</item>
+ <item msgid="1616528372438698248">"Етернет"</item>
<item msgid="9177085807664964627">"VPN"</item>
</string-array>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznat tip mreže"</string>
- <string name="accept" msgid="5447154347815825107">"Prihvati"</string>
- <string name="decline" msgid="6490507610282145874">"Odbij"</string>
- <string name="select_character" msgid="3352797107930786979">"Umetanje znaka"</string>
- <string name="sms_control_title" msgid="4748684259903148341">"Slanje SMS poruka"</string>
- <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; šalje veliki broj SMS poruka. Želite li da dozvolite ovoj aplikaciji da nastavi sa slanjem poruka?"</string>
- <string name="sms_control_yes" msgid="4858845109269524622">"Dozvoli"</string>
- <string name="sms_control_no" msgid="4845717880040355570">"Odbij"</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; želi da pošalje poruku na adresu &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;."</string>
- <string name="sms_short_code_details" msgid="2723725738333388351">"Ovo "<b>"može da prouzrokuje troškove"</b>" na računu za mobilni uređaj."</string>
- <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Ovo će prouzrokovati troškove na računu za mobilni uređaj."</b></string>
- <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Pošalji"</string>
- <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Otkaži"</string>
- <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Zapamti moj izbor"</string>
- <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Ovo možete da promenite kasnije u Podešavanja &gt; Aplikacije"</string>
- <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Uvek dozvoli"</string>
- <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nikada ne dozvoli"</string>
+ <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"непознат тип мреже"</string>
+ <string name="accept" msgid="5447154347815825107">"Прихвати"</string>
+ <string name="decline" msgid="6490507610282145874">"Одбиј"</string>
+ <string name="select_character" msgid="3352797107930786979">"Уметање знака"</string>
+ <string name="sms_control_title" msgid="4748684259903148341">"Слање SMS порука"</string>
+ <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; шаље велики број SMS порука. Желите ли да дозволите овој апликацији да настави са слањем порука?"</string>
+ <string name="sms_control_yes" msgid="4858845109269524622">"Дозволи"</string>
+ <string name="sms_control_no" msgid="4845717880040355570">"Одбиј"</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; жели да пошаље поруку на адресу &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;."</string>
+ <string name="sms_short_code_details" msgid="2723725738333388351">"Ово "<b>"може да проузрокује трошкове"</b>" на рачуну за мобилни уређај."</string>
+ <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Ово ће проузроковати трошкове на рачуну за мобилни уређај."</b></string>
+ <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Пошаљи"</string>
+ <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Откажи"</string>
+ <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Запамти мој избор"</string>
+ <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Ово можете да промените касније у Подешавања &gt; Апликације"</string>
+ <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Увек дозволи"</string>
+ <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Никада не дозволи"</string>
<!-- no translation found for sim_removed_title (1349026474932481037) -->
<skip />
<!-- no translation found for sim_removed_message (8469588437451533845) -->
<skip />
- <string name="sim_done_button" msgid="6464250841528410598">"Gotovo"</string>
+ <string name="sim_done_button" msgid="6464250841528410598">"Готово"</string>
<!-- no translation found for sim_added_title (2976783426741012468) -->
<skip />
- <string name="sim_added_message" msgid="6602906609509958680">"Restartujte uređaj da biste mogli da pristupite mobilnoj mreži."</string>
- <string name="sim_restart_button" msgid="8481803851341190038">"Restartuj"</string>
- <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Aktivirajte mobilnu uslugu"</string>
- <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Preuzmite aplikaciju mobilnog operatera da biste aktivirali novi SIM"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Preuzmite aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> da biste aktivirali novu SIM karticu"</string>
- <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Preuzmite aplikaciju"</string>
- <string name="carrier_app_notification_title" msgid="5815477368072060250">"Nova SIM kartica je umetnuta"</string>
- <string name="carrier_app_notification_text" msgid="6567057546341958637">"Dodirnite za podešavanje"</string>
- <string name="time_picker_dialog_title" msgid="9053376764985220821">"Podesite vreme"</string>
- <string name="date_picker_dialog_title" msgid="5030520449243071926">"Podešavanje datuma"</string>
- <string name="date_time_set" msgid="4603445265164486816">"Podesi"</string>
- <string name="date_time_done" msgid="8363155889402873463">"Gotovo"</string>
- <string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"NOVO: "</font></string>
- <string name="perms_description_app" msgid="2747752389870161996">"Omogućava <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
- <string name="no_permissions" msgid="5729199278862516390">"Nije potrebna nijedna dozvola"</string>
- <string name="perm_costs_money" msgid="749054595022779685">"ovo će vam možda biti naplaćeno"</string>
- <string name="dlg_ok" msgid="5103447663504839312">"Potvrdi"</string>
- <string name="usb_charging_notification_title" msgid="1674124518282666955">"Ovaj uređaj se puni preko USB-a"</string>
- <string name="usb_supplying_notification_title" msgid="5378546632408101811">"Povezani uređaj se puni preko USB-a"</string>
- <string name="usb_mtp_notification_title" msgid="1065989144124499810">"USB prenos datoteka je uključen"</string>
- <string name="usb_ptp_notification_title" msgid="5043437571863443281">"Režim PTP preko USB-a je uključen"</string>
- <string name="usb_tether_notification_title" msgid="8828527870612663771">"USB privezivanje je uključeno"</string>
- <string name="usb_midi_notification_title" msgid="7404506788950595557">"Režim MIDI preko USB-a je uključen"</string>
- <string name="usb_accessory_notification_title" msgid="1385394660861956980">"USB dodatak je povezan"</string>
- <string name="usb_notification_message" msgid="4715163067192110676">"Dodirnite za još opcija."</string>
- <string name="usb_power_notification_message" msgid="7284765627437897702">"Povezani uređaj se puni. Dodirnite za još opcija."</string>
- <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Otkrivena je analogna dodatna oprema za audio sadržaj"</string>
- <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Priključeni uređaj nije kompatibilan sa ovim telefonom. Dodirnite da biste saznali više."</string>
- <string name="adb_active_notification_title" msgid="408390247354560331">"Povezano je otklanjanje grešaka sa USB-a"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Dodirnite da biste ga isključili"</string>
- <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Izaberite da biste onemogućili otklanjanja grešaka sa USB-a."</string>
- <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Bežično otklanjanje grešaka je povezano"</string>
- <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Dodirnite da biste isključili bežično otklanjanje grešaka"</string>
- <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Izaberite da biste onemogućili bežično otklanjanje grešaka."</string>
- <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Omogućen je režim probnog korišćenja"</string>
- <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Obavite resetovanje na fabrička podešavanja da biste onemogućili režim probnog korišćenja."</string>
- <string name="console_running_notification_title" msgid="6087888939261635904">"Serijska konzola je omogućena"</string>
- <string name="console_running_notification_message" msgid="7892751888125174039">"Performanse su smanjene. Da biste onemogući konzolu, proverite pokretački program."</string>
- <string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimentalni MTE je omogućen"</string>
- <string name="mte_override_notification_message" msgid="2441170442725738942">"Ovo može da utiče na performanse i stabilnost. Restartujte da biste onemogućili. Ako je omogućeno pomoću arm64.memtag.bootctl, prvo podesite na Ništa."</string>
- <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"Tečnost ili nečistoća u USB portu"</string>
- <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB port je automatski isključen. Dodirnite da biste saznali više."</string>
- <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"Korišćenje USB porta je dozvoljeno"</string>
- <string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"Telefon više ne otkriva tečnost ili nečistoću."</string>
- <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"Izveštaj o grešci se generiše…"</string>
- <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"Želite li da podelite izveštaj o grešci?"</string>
- <string name="sharing_remote_bugreport_notification_title" msgid="3077385149217638550">"Deli se izveštaj o grešci…"</string>
- <string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"Administrator je zatražio izveštaj o grešci radi lakšeg rešavanja problema u vezi sa ovim uređajem. Aplikacije i podaci mogu da se dele."</string>
- <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DELI"</string>
- <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ODBIJ"</string>
- <string name="select_input_method" msgid="3971267998568587025">"Izbor metoda unosa"</string>
- <string name="show_ime" msgid="6406112007347443383">"Zadržava se na ekranu dok je fizička tastatura aktivna"</string>
- <string name="hardware" msgid="1800597768237606953">"Prikaži virtuelnu tastaturu"</string>
- <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Konfigurišite fizičku tastaturu"</string>
- <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Dodirnite da biste izabrali jezik i raspored"</string>
+ <string name="sim_added_message" msgid="6602906609509958680">"Рестартујте уређај да бисте могли да приступите мобилној мрежи."</string>
+ <string name="sim_restart_button" msgid="8481803851341190038">"Рестартуј"</string>
+ <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Активирајте мобилну услугу"</string>
+ <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Преузмите апликацију мобилног оператера да бисте активирали нови SIM"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Преузмите апликацију <xliff:g id="APP_NAME">%1$s</xliff:g> да бисте активирали нову SIM картицу"</string>
+ <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Преузмите апликацију"</string>
+ <string name="carrier_app_notification_title" msgid="5815477368072060250">"Нова SIM картица је уметнута"</string>
+ <string name="carrier_app_notification_text" msgid="6567057546341958637">"Додирните за подешавање"</string>
+ <string name="time_picker_dialog_title" msgid="9053376764985220821">"Подесите време"</string>
+ <string name="date_picker_dialog_title" msgid="5030520449243071926">"Подешавање датума"</string>
+ <string name="date_time_set" msgid="4603445265164486816">"Подеси"</string>
+ <string name="date_time_done" msgid="8363155889402873463">"Готово"</string>
+ <string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"НОВО: "</font></string>
+ <string name="perms_description_app" msgid="2747752389870161996">"Омогућава <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+ <string name="no_permissions" msgid="5729199278862516390">"Није потребна ниједна дозвола"</string>
+ <string name="perm_costs_money" msgid="749054595022779685">"ово ће вам можда бити наплаћено"</string>
+ <string name="dlg_ok" msgid="5103447663504839312">"Потврди"</string>
+ <string name="usb_charging_notification_title" msgid="1674124518282666955">"Овај уређај се пуни преко USB-а"</string>
+ <string name="usb_supplying_notification_title" msgid="5378546632408101811">"Повезани уређај се пуни преко USB-а"</string>
+ <string name="usb_mtp_notification_title" msgid="1065989144124499810">"USB пренос датотека је укључен"</string>
+ <string name="usb_ptp_notification_title" msgid="5043437571863443281">"Режим PTP преко USB-а је укључен"</string>
+ <string name="usb_tether_notification_title" msgid="8828527870612663771">"USB привезивање је укључено"</string>
+ <string name="usb_midi_notification_title" msgid="7404506788950595557">"Режим MIDI преко USB-а је укључен"</string>
+ <string name="usb_accessory_notification_title" msgid="1385394660861956980">"USB додатак је повезан"</string>
+ <string name="usb_notification_message" msgid="4715163067192110676">"Додирните за још опција."</string>
+ <string name="usb_power_notification_message" msgid="7284765627437897702">"Повезани уређај се пуни. Додирните за још опција."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Откривена је аналогна додатна опрема за аудио садржај"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Прикључени уређај није компатибилан са овим телефоном. Додирните да бисте сазнали више."</string>
+ <string name="adb_active_notification_title" msgid="408390247354560331">"Повезано је отклањање грешака са USB-а"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Додирните да бисте га искључили"</string>
+ <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Изаберите да бисте онемогућили отклањања грешака са USB-а."</string>
+ <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Бежично отклањање грешака је повезано"</string>
+ <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Додирните да бисте искључили бежично отклањање грешака"</string>
+ <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Изаберите да бисте онемогућили бежично отклањање грешака."</string>
+ <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Омогућен је режим пробног коришћења"</string>
+ <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Обавите ресетовање на фабричка подешавања да бисте онемогућили режим пробног коришћења."</string>
+ <string name="console_running_notification_title" msgid="6087888939261635904">"Серијска конзола је омогућена"</string>
+ <string name="console_running_notification_message" msgid="7892751888125174039">"Перформансе су смањене. Да бисте онемогући конзолу, проверите покретачки програм."</string>
+ <string name="mte_override_notification_title" msgid="4731115381962792944">"Експериментални MTE је омогућен"</string>
+ <string name="mte_override_notification_message" msgid="2441170442725738942">"Ово може да утиче на перформансе и стабилност. Рестартујте да бисте онемогућили. Ако је омогућено помоћу arm64.memtag.bootctl, прво подесите на Ништа."</string>
+ <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"Течност или нечистоћа у USB порту"</string>
+ <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт је аутоматски искључен. Додирните да бисте сазнали више."</string>
+ <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"Коришћење USB порта је дозвољено"</string>
+ <string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"Телефон више не открива течност или нечистоћу."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"Извештај о грешци се генерише…"</string>
+ <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"Желите ли да поделите извештај о грешци?"</string>
+ <string name="sharing_remote_bugreport_notification_title" msgid="3077385149217638550">"Дели се извештај о грешци…"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"Администратор је затражио извештај о грешци ради лакшег решавања проблема у вези са овим уређајем. Апликације и подаци могу да се деле."</string>
+ <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ДЕЛИ"</string>
+ <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ОДБИЈ"</string>
+ <string name="select_input_method" msgid="3971267998568587025">"Избор метода уноса"</string>
+ <string name="show_ime" msgid="6406112007347443383">"Задржава се на екрану док је физичка тастатура активна"</string>
+ <string name="hardware" msgid="1800597768237606953">"Прикажи виртуелну тастатуру"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Конфигуришите физичку тастатуру"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Додирните да бисте изабрали језик и распоред"</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">"Prikaz preko drugih aplikacija"</string>
- <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"Aplikacija <xliff:g id="NAME">%s</xliff:g> se prikazuje preko drugih aplikacija"</string>
- <string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> se prikazuje preko drugih apl."</string>
- <string name="alert_windows_notification_message" msgid="6538171456970725333">"Ako ne želite ovu funkciju za <xliff:g id="NAME">%s</xliff:g>, dodirnite da biste otvorili podešavanja i isključili je."</string>
- <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Isključi"</string>
- <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"Proverava se <xliff:g id="NAME">%s</xliff:g>…"</string>
- <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"Pregleda se aktuelni sadržaj"</string>
- <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"Analizira se memorijski prostor za medije"</string>
- <string name="ext_media_new_notification_title" msgid="3517407571407687677">"Novi/a <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> ne radi"</string>
- <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Dodirnite da biste podesili"</string>
- <string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"Izaberite da biste podesili"</string>
- <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"Možda morate da reformatirate uređaj. Dodirnite da biste izbacili."</string>
- <string name="ext_media_ready_notification_message" msgid="7509496364380197369">"Za čuvanje slika, video snimaka, muzike i drugog sadržaja"</string>
- <string name="ext_media_ready_notification_message" product="tv" msgid="8847134811163165935">"Pregledajte medijske fajlove"</string>
- <string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"Problem sa: <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> ne radi"</string>
- <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"Dodirnite da biste ispravili"</string>
- <string name="ext_media_unmountable_notification_message" product="tv" msgid="3003611129979934633">"Medij <xliff:g id="NAME">%s</xliff:g> je oštećen. Izaberite da ga popravite."</string>
- <string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"Možda morate da reformatirate uređaj. Dodirnite da biste izbacili."</string>
- <string name="ext_media_unsupported_notification_title" msgid="3487534182861251401">"Otkriveno: <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> ne radi"</string>
- <string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"Dodirnite da biste podesili."</string>
- <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"Izaberite da biste podesili uređaj <xliff:g id="NAME">%s</xliff:g> u podržanom formatu."</string>
- <string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"Možda morate da reformatirate uređaj"</string>
- <string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"Uređaj <xliff:g id="NAME">%s</xliff:g> je neočekivano uklonjen"</string>
- <string name="ext_media_badremoval_notification_message" msgid="1986514704499809244">"Izbacite medijum pre nego što ga uklonite da ne biste izgubili sadržaj"</string>
- <string name="ext_media_nomedia_notification_title" msgid="742671636376975890">"<xliff:g id="NAME">%s</xliff:g> je uklonjen/a"</string>
- <string name="ext_media_nomedia_notification_message" msgid="2832724384636625852">"Neke funkcije možda neće ispravno raditi. Umetnite nov memorijski uređaj."</string>
- <string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"Izbacuje se <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Ne uklanjajte"</string>
- <string name="ext_media_init_action" msgid="2312974060585056709">"Aktiviraj"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"Izbaci"</string>
- <string name="ext_media_browse_action" msgid="344865351947079139">"Istraži"</string>
- <string name="ext_media_seamless_action" msgid="8837030226009268080">"Promenite izlaz"</string>
- <string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> nedostaje"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"Ponovo umetnite uređaj"</string>
- <string name="ext_media_move_specific_title" msgid="8492118544775964250">"Prenosi se <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="ext_media_move_title" msgid="2682741525619033637">"Podaci se prenose"</string>
- <string name="ext_media_move_success_title" msgid="4901763082647316767">"Prenos sadržaja je gotov"</string>
- <string name="ext_media_move_success_message" msgid="9159542002276982979">"Sadržaj je premešen na: <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="ext_media_move_failure_title" msgid="3184577479181333665">"Premeštanje sadržaja nije uspelo"</string>
- <string name="ext_media_move_failure_message" msgid="4197306718121869335">"Probajte da ponovo premestite sadržaj"</string>
- <string name="ext_media_status_removed" msgid="241223931135751691">"Uklonjen je"</string>
- <string name="ext_media_status_unmounted" msgid="8145812017295835941">"Izbačen je"</string>
- <string name="ext_media_status_checking" msgid="159013362442090347">"Proverava se..."</string>
- <string name="ext_media_status_mounted" msgid="3459448555811203459">"Spreman je"</string>
- <string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"Samo za čitanje"</string>
- <string name="ext_media_status_bad_removal" msgid="508448566481406245">"Uklonjen je na nebezbedan način"</string>
- <string name="ext_media_status_unmountable" msgid="7043574843541087748">"Oštećen je"</string>
- <string name="ext_media_status_unsupported" msgid="5460509911660539317">"Nije podržan"</string>
- <string name="ext_media_status_ejecting" msgid="7532403368044013797">"Izbacuje se..."</string>
- <string name="ext_media_status_formatting" msgid="774148701503179906">"Formatira se..."</string>
- <string name="ext_media_status_missing" msgid="6520746443048867314">"Nije umetnut"</string>
- <string name="activity_list_empty" msgid="4219430010716034252">"Nije pronađena nijedna podudarna aktivnost."</string>
- <string name="permlab_route_media_output" msgid="8048124531439513118">"usmeravanje izlaza medija"</string>
- <string name="permdesc_route_media_output" msgid="1759683269387729675">"Dozvoljava aplikaciji da usmerava izlaz medija na druge spoljne uređaje."</string>
- <string name="permlab_readInstallSessions" msgid="7279049337895583621">"čitanje sesija instaliranja"</string>
- <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"Dozvoljava aplikaciji da čita sesije instaliranja. To joj dozvoljava da vidi detalje o aktivnim instalacijama paketa."</string>
- <string name="permlab_requestInstallPackages" msgid="7600020863445351154">"zahtevanje paketa za instaliranje"</string>
- <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"Omogućava da aplikacija zahteva instalaciju paketa."</string>
- <string name="permlab_requestDeletePackages" msgid="2541172829260106795">"zahtevanje brisanja paketa"</string>
- <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Omogućava da aplikacija zahteva brisanje paketa."</string>
- <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"traženje dozvole za ignorisanje optimizacija baterije"</string>
- <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Dozvoljava aplikaciji da traži dozvolu za ignorisanje optimizacija baterije za tu aplikaciju."</string>
- <string name="permlab_queryAllPackages" msgid="2928450604653281650">"slanje upita za sve pakete"</string>
- <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Dozvoljava aplikaciji da vidi sve instalirane pakete."</string>
- <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dodirnite dvaput za kontrolu zumiranja"</string>
- <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nije moguće dodati vidžet."</string>
- <string name="ime_action_go" msgid="5536744546326495436">"Idi"</string>
- <string name="ime_action_search" msgid="4501435960587287668">"Pretraži"</string>
- <string name="ime_action_send" msgid="8456843745664334138">"Pošalji"</string>
- <string name="ime_action_next" msgid="4169702997635728543">"Dalje"</string>
- <string name="ime_action_done" msgid="6299921014822891569">"Gotovo"</string>
- <string name="ime_action_previous" msgid="6548799326860401611">"Prethodno"</string>
- <string name="ime_action_default" msgid="8265027027659800121">"Izvrši"</string>
- <string name="dial_number_using" msgid="6060769078933953531">"Biraj broj\nkoristeći <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="create_contact_using" msgid="6200708808003692594">"Napravite kontakt\nkoristeći <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Sledeće aplikacije zahtevaju dozvolu za pristup nalogu, kako sada, tako i ubuduće."</string>
- <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Želite da odobrite ovaj zahtev?"</string>
- <string name="grant_permissions_header_text" msgid="3420736827804657201">"Zahtev za pristup"</string>
- <string name="allow" msgid="6195617008611933762">"Dozvoli"</string>
- <string name="deny" msgid="6632259981847676572">"Odbij"</string>
- <string name="permission_request_notification_title" msgid="1810025922441048273">"Zatražena je dozvola"</string>
- <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Zatražena je dozvola\nza nalog <xliff:g id="ACCOUNT">%s</xliff:g>"</string>
- <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"<xliff:g id="APP">%1$s</xliff:g> traži dozvolu \nza nalog <xliff:g id="ACCOUNT">%2$s</xliff:g>."</string>
- <string name="forward_intent_to_owner" msgid="4620359037192871015">"Koristite ovu aplikaciju izvan poslovnog profila"</string>
- <string name="forward_intent_to_work" msgid="3620262405636021151">"Koristite ovu aplikaciju na poslovnom profilu"</string>
- <string name="input_method_binding_label" msgid="1166731601721983656">"Metod unosa"</string>
- <string name="sync_binding_label" msgid="469249309424662147">"Sinhronizacija"</string>
- <string name="accessibility_binding_label" msgid="1974602776545801715">"Pristupačnost"</string>
- <string name="wallpaper_binding_label" msgid="1197440498000786738">"Pozadina"</string>
- <string name="chooser_wallpaper" msgid="3082405680079923708">"Promena pozadine"</string>
- <string name="notification_listener_binding_label" msgid="2702165274471499713">"Monitor obaveštenja"</string>
- <string name="vr_listener_binding_label" msgid="8013112996671206429">"Obrađivač za virtuelnu realnost"</string>
- <string name="condition_provider_service_binding_label" msgid="8490641013951857673">"Dobavljač uslova"</string>
- <string name="notification_ranker_binding_label" msgid="432708245635563763">"Usluga rangiranja obaveštenja"</string>
- <string name="vpn_title" msgid="5906991595291514182">"VPN je aktiviran"</string>
- <string name="vpn_title_long" msgid="6834144390504619998">"Aplikacija <xliff:g id="APP">%s</xliff:g> je aktivirala VPN"</string>
- <string name="vpn_text" msgid="2275388920267251078">"Dodirnite da biste upravljali mrežom."</string>
- <string name="vpn_text_long" msgid="278540576806169831">"Povezano sa sesijom <xliff:g id="SESSION">%s</xliff:g>. Dodirnite da biste upravljali mrežom."</string>
- <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"Povezivanje stalno uključenog VPN-a..."</string>
- <string name="vpn_lockdown_connected" msgid="2853127976590658469">"Stalno uključen VPN je povezan"</string>
- <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Veza sa uvek uključenim VPN-om je prekinuta"</string>
- <string name="vpn_lockdown_error" msgid="4453048646854247947">"Povezivanje na stalno uključeni VPN nije uspelo"</string>
- <string name="vpn_lockdown_config" msgid="8331697329868252169">"Promenite podešavanja VPN-a"</string>
- <string name="upload_file" msgid="8651942222301634271">"Odaberi fajl"</string>
- <string name="no_file_chosen" msgid="4146295695162318057">"Nije izabrana nijedna datoteka"</string>
- <string name="reset" msgid="3865826612628171429">"Resetuj"</string>
- <string name="submit" msgid="862795280643405865">"Pošalji"</string>
- <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacija za vožnju je pokrenuta"</string>
- <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Dodirnite da biste izašli iz aplikacije za vožnju."</string>
- <string name="back_button_label" msgid="4078224038025043387">"Nazad"</string>
+ <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Приказ преко других апликација"</string>
+ <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"Апликација <xliff:g id="NAME">%s</xliff:g> се приказује преко других апликација"</string>
+ <string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> се приказује преко других апл."</string>
+ <string name="alert_windows_notification_message" msgid="6538171456970725333">"Ако не желите ову функцију за <xliff:g id="NAME">%s</xliff:g>, додирните да бисте отворили подешавања и искључили је."</string>
+ <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Искључи"</string>
+ <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"Проверава се <xliff:g id="NAME">%s</xliff:g>…"</string>
+ <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"Прегледа се актуелни садржај"</string>
+ <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"Анализира се меморијски простор за медије"</string>
+ <string name="ext_media_new_notification_title" msgid="3517407571407687677">"Нови/а <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> не ради"</string>
+ <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Додирните да бисте подесили"</string>
+ <string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"Изаберите да бисте подесили"</string>
+ <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"Можда морате да реформатирате уређај. Додирните да бисте избацили."</string>
+ <string name="ext_media_ready_notification_message" msgid="7509496364380197369">"За чување слика, видео снимака, музике и другог садржаја"</string>
+ <string name="ext_media_ready_notification_message" product="tv" msgid="8847134811163165935">"Прегледајте медијске фајлове"</string>
+ <string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"Проблем са: <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> не ради"</string>
+ <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"Додирните да бисте исправили"</string>
+ <string name="ext_media_unmountable_notification_message" product="tv" msgid="3003611129979934633">"Медиј <xliff:g id="NAME">%s</xliff:g> је оштећен. Изаберите да га поправите."</string>
+ <string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"Можда морате да реформатирате уређај. Додирните да бисте избацили."</string>
+ <string name="ext_media_unsupported_notification_title" msgid="3487534182861251401">"Откривенo: <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> не ради"</string>
+ <string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"Додирните да бисте подесили."</string>
+ <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"Изаберите да бисте подесили уређај <xliff:g id="NAME">%s</xliff:g> у подржаном формату."</string>
+ <string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"Можда морате да реформатирате уређај"</string>
+ <string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"Уређај <xliff:g id="NAME">%s</xliff:g> је неочекивано уклоњен"</string>
+ <string name="ext_media_badremoval_notification_message" msgid="1986514704499809244">"Избаците медијум пре него што га уклоните да не бисте изгубили садржај"</string>
+ <string name="ext_media_nomedia_notification_title" msgid="742671636376975890">"<xliff:g id="NAME">%s</xliff:g> је уклоњен/а"</string>
+ <string name="ext_media_nomedia_notification_message" msgid="2832724384636625852">"Неке функције можда неће исправно радити. Уметните нов меморијски уређај."</string>
+ <string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"Избацује се <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Не уклањајте"</string>
+ <string name="ext_media_init_action" msgid="2312974060585056709">"Активирај"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"Избаци"</string>
+ <string name="ext_media_browse_action" msgid="344865351947079139">"Истражи"</string>
+ <string name="ext_media_seamless_action" msgid="8837030226009268080">"Промените излаз"</string>
+ <string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> недостаје"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"Поново уметните уређај"</string>
+ <string name="ext_media_move_specific_title" msgid="8492118544775964250">"Преноси се <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="ext_media_move_title" msgid="2682741525619033637">"Подаци се преносе"</string>
+ <string name="ext_media_move_success_title" msgid="4901763082647316767">"Пренос садржаја је готов"</string>
+ <string name="ext_media_move_success_message" msgid="9159542002276982979">"Садржај је премешен на: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="ext_media_move_failure_title" msgid="3184577479181333665">"Премештање садржаја није успело"</string>
+ <string name="ext_media_move_failure_message" msgid="4197306718121869335">"Пробајте да поново преместите садржај"</string>
+ <string name="ext_media_status_removed" msgid="241223931135751691">"Уклоњен је"</string>
+ <string name="ext_media_status_unmounted" msgid="8145812017295835941">"Избачен је"</string>
+ <string name="ext_media_status_checking" msgid="159013362442090347">"Проверава се..."</string>
+ <string name="ext_media_status_mounted" msgid="3459448555811203459">"Спреман је"</string>
+ <string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"Само за читање"</string>
+ <string name="ext_media_status_bad_removal" msgid="508448566481406245">"Уклоњен је на небезбедан начин"</string>
+ <string name="ext_media_status_unmountable" msgid="7043574843541087748">"Оштећен је"</string>
+ <string name="ext_media_status_unsupported" msgid="5460509911660539317">"Није подржан"</string>
+ <string name="ext_media_status_ejecting" msgid="7532403368044013797">"Избацује се..."</string>
+ <string name="ext_media_status_formatting" msgid="774148701503179906">"Форматира се..."</string>
+ <string name="ext_media_status_missing" msgid="6520746443048867314">"Није уметнут"</string>
+ <string name="activity_list_empty" msgid="4219430010716034252">"Није пронађена ниједна подударна активност."</string>
+ <string name="permlab_route_media_output" msgid="8048124531439513118">"усмеравање излаза медија"</string>
+ <string name="permdesc_route_media_output" msgid="1759683269387729675">"Дозвољава апликацији да усмерава излаз медија на друге спољне уређаје."</string>
+ <string name="permlab_readInstallSessions" msgid="7279049337895583621">"читање сесија инсталирања"</string>
+ <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"Дозвољава апликацији да чита сесије инсталирања. То јој дозвољава да види детаље о активним инсталацијама пакета."</string>
+ <string name="permlab_requestInstallPackages" msgid="7600020863445351154">"захтевање пакета за инсталирање"</string>
+ <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"Омогућава да апликација захтева инсталацију пакета."</string>
+ <string name="permlab_requestDeletePackages" msgid="2541172829260106795">"захтевање брисања пакета"</string>
+ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Омогућава да апликација захтева брисање пакета."</string>
+ <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"тражење дозволе за игнорисање оптимизација батерије"</string>
+ <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Дозвољава апликацији да тражи дозволу за игнорисање оптимизација батерије за ту апликацију."</string>
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"слање упита за све пакете"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозвољава апликацији да види све инсталиране пакете."</string>
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Додирните двапут за контролу зумирања"</string>
+ <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Није могуће додати виџет."</string>
+ <string name="ime_action_go" msgid="5536744546326495436">"Иди"</string>
+ <string name="ime_action_search" msgid="4501435960587287668">"Претражи"</string>
+ <string name="ime_action_send" msgid="8456843745664334138">"Пошаљи"</string>
+ <string name="ime_action_next" msgid="4169702997635728543">"Даље"</string>
+ <string name="ime_action_done" msgid="6299921014822891569">"Готово"</string>
+ <string name="ime_action_previous" msgid="6548799326860401611">"Претходно"</string>
+ <string name="ime_action_default" msgid="8265027027659800121">"Изврши"</string>
+ <string name="dial_number_using" msgid="6060769078933953531">"Бирај број\nкористећи <xliff:g id="NUMBER">%s</xliff:g>"</string>
+ <string name="create_contact_using" msgid="6200708808003692594">"Направите контакт\nкористећи <xliff:g id="NUMBER">%s</xliff:g>"</string>
+ <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Следеће апликације захтевају дозволу за приступ налогу, како сада, тако и убудуће."</string>
+ <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Желите да одобрите овај захтев?"</string>
+ <string name="grant_permissions_header_text" msgid="3420736827804657201">"Захтев за приступ"</string>
+ <string name="allow" msgid="6195617008611933762">"Дозволи"</string>
+ <string name="deny" msgid="6632259981847676572">"Одбиј"</string>
+ <string name="permission_request_notification_title" msgid="1810025922441048273">"Затражена је дозвола"</string>
+ <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Затражена је дозвола\nза налог <xliff:g id="ACCOUNT">%s</xliff:g>"</string>
+ <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"<xliff:g id="APP">%1$s</xliff:g> тражи дозволу \nза налог <xliff:g id="ACCOUNT">%2$s</xliff:g>."</string>
+ <string name="forward_intent_to_owner" msgid="4620359037192871015">"Користите ову апликацију изван пословног профила"</string>
+ <string name="forward_intent_to_work" msgid="3620262405636021151">"Користите ову апликацију на пословном профилу"</string>
+ <string name="input_method_binding_label" msgid="1166731601721983656">"Метод уноса"</string>
+ <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="notification_listener_binding_label" msgid="2702165274471499713">"Монитор обавештења"</string>
+ <string name="vr_listener_binding_label" msgid="8013112996671206429">"Обрађивач за виртуелну реалност"</string>
+ <string name="condition_provider_service_binding_label" msgid="8490641013951857673">"Добављач услова"</string>
+ <string name="notification_ranker_binding_label" msgid="432708245635563763">"Услуга рангирања обавештења"</string>
+ <string name="vpn_title" msgid="5906991595291514182">"VPN је активиран"</string>
+ <string name="vpn_title_long" msgid="6834144390504619998">"Апликација <xliff:g id="APP">%s</xliff:g> је активирала VPN"</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_disconnected" msgid="5573611651300764955">"Веза са увек укљученим VPN-ом је прекинута"</string>
+ <string name="vpn_lockdown_error" msgid="4453048646854247947">"Повезивање на стално укључени VPN није успело"</string>
+ <string name="vpn_lockdown_config" msgid="8331697329868252169">"Промените подешавања VPN-а"</string>
+ <string name="upload_file" msgid="8651942222301634271">"Одабери фајл"</string>
+ <string name="no_file_chosen" msgid="4146295695162318057">"Није изабрана ниједна датотека"</string>
+ <string name="reset" msgid="3865826612628171429">"Ресетуј"</string>
+ <string name="submit" msgid="862795280643405865">"Пошаљи"</string>
+ <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Апликација за вожњу је покренута"</string>
+ <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Додирните да бисте изашли из апликације за вожњу."</string>
+ <string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
<string name="next_button_label" msgid="6040209156399907780">"Next"</string>
- <string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
- <string name="no_matches" msgid="6472699895759164599">"Nema podudaranja"</string>
- <string name="find_on_page" msgid="5400537367077438198">"Pronađi na stranici"</string>
- <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# podudaranje}one{# od {total}}few{# od {total}}other{# od {total}}}"</string>
- <string name="action_mode_done" msgid="2536182504764803222">"Gotovo"</string>
- <string name="progress_erasing" msgid="6891435992721028004">"Briše se deljeni memorijski prostor…"</string>
- <string name="share" msgid="4157615043345227321">"Deli"</string>
- <string name="find" msgid="5015737188624767706">"Pronađi"</string>
- <string name="websearch" msgid="5624340204512793290">"Veb-pretraga"</string>
- <string name="find_next" msgid="5341217051549648153">"Pronađi sledeće"</string>
- <string name="find_previous" msgid="4405898398141275532">"Pronađi prethodno"</string>
- <string name="gpsNotifTicker" msgid="3207361857637620780">"Zahtev za lokaciju od korisnika <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="1590033371665669570">"Zahtev za lokaciju"</string>
- <string name="gpsNotifMessage" msgid="7346649122793758032">"Zahteva <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
- <string name="gpsVerifYes" msgid="3719843080744112940">"Da"</string>
- <string name="gpsVerifNo" msgid="1671201856091564741">"Ne"</string>
- <string name="sync_too_many_deletes" msgid="6999440774578705300">"Premašeno je ograničenje za brisanje"</string>
- <string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"Postoje izbrisane stavke (<xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g>) za <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, nalog <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. Šta želite da uradite?"</string>
- <string name="sync_really_delete" msgid="5657871730315579051">"Izbriši stavke"</string>
- <string name="sync_undo_deletes" msgid="5786033331266418896">"Opozovi brisanja"</string>
- <string name="sync_do_nothing" msgid="4528734662446469646">"Ne radi ništa za sada"</string>
- <string name="choose_account_label" msgid="5557833752759831548">"Izaberite nalog"</string>
- <string name="add_account_label" msgid="4067610644298737417">"Dodaj nalog"</string>
- <string name="add_account_button_label" msgid="322390749416414097">"Dodaj nalog"</string>
- <string name="number_picker_increment_button" msgid="7621013714795186298">"Povećavanje"</string>
- <string name="number_picker_decrement_button" msgid="5116948444762708204">"Smanjivanje"</string>
- <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> dodirnite i zadržite."</string>
- <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Prevucite nagore da biste povećali, a nadole da biste smanjili."</string>
- <string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Povećavanje minuta"</string>
- <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Smanjivanje minuta"</string>
- <string name="time_picker_increment_hour_button" msgid="3063572723197178242">"Povećavanje sati"</string>
- <string name="time_picker_decrement_hour_button" msgid="584101766855054412">"Smanjivanje sati"</string>
- <string name="time_picker_increment_set_pm_button" msgid="5889149366900376419">"Podesi po podne"</string>
- <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"Podesi pre podne"</string>
- <string name="date_picker_increment_month_button" msgid="3447263316096060309">"Povećavanje meseca"</string>
- <string name="date_picker_decrement_month_button" msgid="6531888937036883014">"Smanjivanje meseca"</string>
- <string name="date_picker_increment_day_button" msgid="4349336637188534259">"Povećavanje dana"</string>
- <string name="date_picker_decrement_day_button" msgid="6840253837656637248">"Smanjivanje dana"</string>
- <string name="date_picker_increment_year_button" msgid="7608128783435372594">"Povećavanje godine"</string>
- <string name="date_picker_decrement_year_button" msgid="4102586521754172684">"Smanjivanje godine"</string>
- <string name="date_picker_prev_month_button" msgid="3418694374017868369">"Prethodni mesec"</string>
- <string name="date_picker_next_month_button" msgid="4858207337779144840">"Sledeći mesec"</string>
+ <string name="skip_button_label" msgid="3566599811326688389">"Прескочи"</string>
+ <string name="no_matches" msgid="6472699895759164599">"Нема подударања"</string>
+ <string name="find_on_page" msgid="5400537367077438198">"Пронађи на страници"</string>
+ <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# подударање}one{# од {total}}few{# од {total}}other{# од {total}}}"</string>
+ <string name="action_mode_done" msgid="2536182504764803222">"Готово"</string>
+ <string name="progress_erasing" msgid="6891435992721028004">"Брише се дељени меморијски простор…"</string>
+ <string name="share" msgid="4157615043345227321">"Дели"</string>
+ <string name="find" msgid="5015737188624767706">"Пронађи"</string>
+ <string name="websearch" msgid="5624340204512793290">"Веб-претрага"</string>
+ <string name="find_next" msgid="5341217051549648153">"Пронађи следеће"</string>
+ <string name="find_previous" msgid="4405898398141275532">"Пронађи претходно"</string>
+ <string name="gpsNotifTicker" msgid="3207361857637620780">"Захтев за локацију од корисника <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="gpsNotifTitle" msgid="1590033371665669570">"Захтев за локацију"</string>
+ <string name="gpsNotifMessage" msgid="7346649122793758032">"Захтева <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
+ <string name="gpsVerifYes" msgid="3719843080744112940">"Да"</string>
+ <string name="gpsVerifNo" msgid="1671201856091564741">"Не"</string>
+ <string name="sync_too_many_deletes" msgid="6999440774578705300">"Премашено је ограничење за брисање"</string>
+ <string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"Постоје избрисане ставке (<xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g>) за <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, налог <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. Шта желите да урадите?"</string>
+ <string name="sync_really_delete" msgid="5657871730315579051">"Избриши ставке"</string>
+ <string name="sync_undo_deletes" msgid="5786033331266418896">"Опозови брисања"</string>
+ <string name="sync_do_nothing" msgid="4528734662446469646">"Не ради ништа за сада"</string>
+ <string name="choose_account_label" msgid="5557833752759831548">"Изаберите налог"</string>
+ <string name="add_account_label" msgid="4067610644298737417">"Додај налог"</string>
+ <string name="add_account_button_label" msgid="322390749416414097">"Додај налог"</string>
+ <string name="number_picker_increment_button" msgid="7621013714795186298">"Повећавање"</string>
+ <string name="number_picker_decrement_button" msgid="5116948444762708204">"Смањивање"</string>
+ <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> додирните и задржите."</string>
+ <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Превуците нагоре да бисте повећали, а надоле да бисте смањили."</string>
+ <string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Повећавање минута"</string>
+ <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Смањивање минута"</string>
+ <string name="time_picker_increment_hour_button" msgid="3063572723197178242">"Повећавање сати"</string>
+ <string name="time_picker_decrement_hour_button" msgid="584101766855054412">"Смањивање сати"</string>
+ <string name="time_picker_increment_set_pm_button" msgid="5889149366900376419">"Подеси по подне"</string>
+ <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"Подеси пре подне"</string>
+ <string name="date_picker_increment_month_button" msgid="3447263316096060309">"Повећавање месеца"</string>
+ <string name="date_picker_decrement_month_button" msgid="6531888937036883014">"Смањивање месеца"</string>
+ <string name="date_picker_increment_day_button" msgid="4349336637188534259">"Повећавање дана"</string>
+ <string name="date_picker_decrement_day_button" msgid="6840253837656637248">"Смањивање дана"</string>
+ <string name="date_picker_increment_year_button" msgid="7608128783435372594">"Повећавање године"</string>
+ <string name="date_picker_decrement_year_button" msgid="4102586521754172684">"Смањивање године"</string>
+ <string name="date_picker_prev_month_button" msgid="3418694374017868369">"Претходни месец"</string>
+ <string name="date_picker_next_month_button" msgid="4858207337779144840">"Следећи месец"</string>
<string name="keyboardview_keycode_alt" msgid="8997420058584292385">"Alt"</string>
- <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Otkaži"</string>
- <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Izbriši"</string>
- <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Gotovo"</string>
- <string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Promena režima"</string>
+ <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Откажи"</string>
+ <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Избриши"</string>
+ <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Готово"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Промена режима"</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">"Izaberite aplikaciju"</string>
- <string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"Nije moguće pokrenuti <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="shareactionprovider_share_with" msgid="2753089758467748982">"Deli sa"</string>
- <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"Deli sa aplikacijom <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="content_description_sliding_handle" msgid="982510275422590757">"Klizna ručica. Dodirnite i zadržite."</string>
- <string name="description_target_unlock_tablet" msgid="7431571180065859551">"Prevucite da biste otključali."</string>
- <string name="action_bar_home_description" msgid="1501655419158631974">"Kretanje do Početne"</string>
- <string name="action_bar_up_description" msgid="6611579697195026932">"Kretanje nagore"</string>
- <string name="action_menu_overflow_description" msgid="4579536843510088170">"Još opcija"</string>
+ <string name="activitychooserview_choose_application" msgid="3500574466367891463">"Изаберите апликацију"</string>
+ <string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"Није могуће покренути <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+ <string name="shareactionprovider_share_with" msgid="2753089758467748982">"Дели са"</string>
+ <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"Дели са апликацијом <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+ <string name="content_description_sliding_handle" msgid="982510275422590757">"Клизна ручица. Додирните и задржите."</string>
+ <string name="description_target_unlock_tablet" msgid="7431571180065859551">"Превуците да бисте откључали."</string>
+ <string name="action_bar_home_description" msgid="1501655419158631974">"Кретање до Почетне"</string>
+ <string name="action_bar_up_description" msgid="6611579697195026932">"Кретање нагоре"</string>
+ <string name="action_menu_overflow_description" msgid="4579536843510088170">"Још опција"</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>
- <string name="storage_internal" msgid="8490227947584914460">"Unutrašnji deljeni memorijski prostor"</string>
- <string name="storage_sd_card" msgid="3404740277075331881">"SD kartica"</string>
- <string name="storage_sd_card_label" msgid="7526153141147470509">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartica"</string>
- <string name="storage_usb_drive" msgid="448030813201444573">"USB disk"</string>
- <string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB disk"</string>
- <string name="storage_usb" msgid="2391213347883616886">"USB memorija"</string>
- <string name="extract_edit_menu_button" msgid="63954536535863040">"Izmeni"</string>
- <string name="data_usage_warning_title" msgid="9034893717078325845">"Upozorenje na potrošnju podataka"</string>
- <string name="data_usage_warning_body" msgid="1669325367188029454">"Potrošili ste <xliff:g id="APP">%s</xliff:g> podataka"</string>
- <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Dostigli ste ograničenje podataka"</string>
- <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Nema više WiFi podataka"</string>
- <string name="data_usage_limit_body" msgid="3567699582000085710">"Podaci su pauzirani tokom ostatka ciklusa"</string>
- <string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Potrošili ste mobilne podatke"</string>
- <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Potrošili ste WiFi podatke"</string>
- <string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Prekoračili ste <xliff:g id="SIZE">%s</xliff:g> od podešenog ograničenja"</string>
- <string name="data_usage_restricted_title" msgid="126711424380051268">"Pozadinski podaci su ograničeni"</string>
- <string name="data_usage_restricted_body" msgid="5338694433686077733">"Dodirnite za uklanjanje ograničenja."</string>
- <string name="data_usage_rapid_title" msgid="2950192123248740375">"Velika potrošnja mob. podataka"</string>
- <string name="data_usage_rapid_body" msgid="3886676853263693432">"Aplikacije su potrošile više podataka nego obično"</string>
- <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"Aplikacija <xliff:g id="APP">%s</xliff:g> je potrošila više podataka nego obično"</string>
- <string name="ssl_certificate" msgid="5690020361307261997">"Bezbednosni sertifikat"</string>
- <string name="ssl_certificate_is_valid" msgid="7293675884598527081">"Ovaj sertifikat je važeći."</string>
- <string name="issued_to" msgid="5975877665505297662">"Izdato za:"</string>
- <string name="common_name" msgid="1486334593631798443">"Uobičajeni naziv:"</string>
- <string name="org_name" msgid="7526331696464255245">"Organizacija:"</string>
- <string name="org_unit" msgid="995934486977223076">"Organizaciona jedinica:"</string>
- <string name="issued_by" msgid="7872459822431585684">"Izdao/la:"</string>
- <string name="validity_period" msgid="1717724283033175968">"Važnost:"</string>
- <string name="issued_on" msgid="5855489688152497307">"Izdato:"</string>
- <string name="expires_on" msgid="1623640879705103121">"Ističe:"</string>
- <string name="serial_number" msgid="3479576915806623429">"Serijski broj:"</string>
- <string name="fingerprints" msgid="148690767172613723">"Digitalni otisci:"</string>
- <string name="sha256_fingerprint" msgid="7103976380961964600">"SHA-256 otisak prsta:"</string>
- <string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1 otisak prsta:"</string>
- <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Prikaži sve"</string>
- <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Izbor aktivnosti"</string>
- <string name="share_action_provider_share_with" msgid="1904096863622941880">"Deli sa"</string>
- <string name="sending" msgid="206925243621664438">"Slanje..."</string>
- <string name="launchBrowserDefault" msgid="6328349989932924119">"Želite li da pokrenete pregledač?"</string>
- <string name="SetupCallDefault" msgid="5581740063237175247">"Želite li da prihvatite poziv?"</string>
- <string name="activity_resolver_use_always" msgid="5575222334666843269">"Uvek"</string>
- <string name="activity_resolver_use_once" msgid="948462794469672658">"Samo jednom"</string>
- <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s ne podržava poslovni profil"</string>
- <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Tablet"</string>
- <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="8124229858618975">"Spoljni uređaj"</string>
- <string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Slušalice"</string>
+ <string name="storage_internal" msgid="8490227947584914460">"Унутрашњи дељени меморијски простор"</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>
+ <string name="storage_usb" msgid="2391213347883616886">"USB меморија"</string>
+ <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_wifi_limit_title" msgid="2069698056520812232">"Нема више WiFi података"</string>
+ <string name="data_usage_limit_body" msgid="3567699582000085710">"Подаци су паузирани током остатка циклуса"</string>
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Потрошили сте мобилне податке"</string>
+ <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Потрошили сте WiFi податке"</string>
+ <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_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>
+ <string name="ssl_certificate_is_valid" msgid="7293675884598527081">"Овај сертификат је важећи."</string>
+ <string name="issued_to" msgid="5975877665505297662">"Издато за:"</string>
+ <string name="common_name" msgid="1486334593631798443">"Уобичајени назив:"</string>
+ <string name="org_name" msgid="7526331696464255245">"Организација:"</string>
+ <string name="org_unit" msgid="995934486977223076">"Организациона јединица:"</string>
+ <string name="issued_by" msgid="7872459822431585684">"Издао/ла:"</string>
+ <string name="validity_period" msgid="1717724283033175968">"Важност:"</string>
+ <string name="issued_on" msgid="5855489688152497307">"Издато:"</string>
+ <string name="expires_on" msgid="1623640879705103121">"Истиче:"</string>
+ <string name="serial_number" msgid="3479576915806623429">"Серијски број:"</string>
+ <string name="fingerprints" msgid="148690767172613723">"Дигитални отисци:"</string>
+ <string name="sha256_fingerprint" msgid="7103976380961964600">"SHA-256 отисак прста:"</string>
+ <string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1 отисак прста:"</string>
+ <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Прикажи све"</string>
+ <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Избор активности"</string>
+ <string name="share_action_provider_share_with" msgid="1904096863622941880">"Дели са"</string>
+ <string name="sending" msgid="206925243621664438">"Слање..."</string>
+ <string name="launchBrowserDefault" msgid="6328349989932924119">"Желите ли да покренете прегледач?"</string>
+ <string name="SetupCallDefault" msgid="5581740063237175247">"Желите ли да прихватите позив?"</string>
+ <string name="activity_resolver_use_always" msgid="5575222334666843269">"Увек"</string>
+ <string name="activity_resolver_use_once" msgid="948462794469672658">"Само једном"</string>
+ <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s не подржава пословни профил"</string>
+ <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Таблет"</string>
+ <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="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">"Sistem"</string>
- <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Bluetooth audio"</string>
- <string name="wireless_display_route_description" msgid="8297563323032966831">"Bežični ekran"</string>
- <string name="media_route_button_content_description" msgid="2299223698196869956">"Prebacuj"</string>
- <string name="media_route_chooser_title" msgid="6646594924991269208">"Povežite sa uređajem"</string>
- <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Prebacite ekran na uređaj"</string>
- <string name="media_route_chooser_searching" msgid="6119673534251329535">"Traženje uređaja…"</string>
- <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Podešavanja"</string>
- <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Prekini vezu"</string>
- <string name="media_route_status_scanning" msgid="8045156315309594482">"Skeniranje..."</string>
- <string name="media_route_status_connecting" msgid="5845597961412010540">"Povezuje se..."</string>
- <string name="media_route_status_available" msgid="1477537663492007608">"Dostupna"</string>
- <string name="media_route_status_not_available" msgid="480912417977515261">"Nisu dostupne"</string>
- <string name="media_route_status_in_use" msgid="6684112905244944724">"U upotrebi"</string>
- <string name="display_manager_built_in_display_name" msgid="1015775198829722440">"Ugrađeni ekran"</string>
- <string name="display_manager_hdmi_display_name" msgid="1022758026251534975">"HDMI ekran"</string>
- <string name="display_manager_overlay_display_name" msgid="5306088205181005861">"Postavljeni element br. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <string name="default_audio_route_category_name" msgid="5241740395748134483">"Систем"</string>
+ <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Bluetooth аудио"</string>
+ <string name="wireless_display_route_description" msgid="8297563323032966831">"Бежични екран"</string>
+ <string name="media_route_button_content_description" msgid="2299223698196869956">"Пребацуј"</string>
+ <string name="media_route_chooser_title" msgid="6646594924991269208">"Повежите са уређајем"</string>
+ <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Пребаците екран на уређај"</string>
+ <string name="media_route_chooser_searching" msgid="6119673534251329535">"Тражење уређаја…"</string>
+ <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Подешавања"</string>
+ <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Прекини везу"</string>
+ <string name="media_route_status_scanning" msgid="8045156315309594482">"Скенирање..."</string>
+ <string name="media_route_status_connecting" msgid="5845597961412010540">"Повезује се..."</string>
+ <string name="media_route_status_available" msgid="1477537663492007608">"Доступна"</string>
+ <string name="media_route_status_not_available" msgid="480912417977515261">"Нису доступне"</string>
+ <string name="media_route_status_in_use" msgid="6684112905244944724">"У употреби"</string>
+ <string name="display_manager_built_in_display_name" msgid="1015775198829722440">"Уграђени екран"</string>
+ <string name="display_manager_hdmi_display_name" msgid="1022758026251534975">"HDMI екран"</string>
+ <string name="display_manager_overlay_display_name" msgid="5306088205181005861">"Постављени елемент бр. <xliff:g id="ID">%1$d</xliff:g>"</string>
<string name="display_manager_overlay_display_title" msgid="1480158037150469170">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>×<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
- <string name="display_manager_overlay_display_secure_suffix" msgid="2810034719482834679">", bezbedno"</string>
- <string name="kg_forgot_pattern_button_text" msgid="406145459223122537">"Zaboravljeni šablon"</string>
- <string name="kg_wrong_pattern" msgid="1342812634464179931">"Pogrešan šablon"</string>
- <string name="kg_wrong_password" msgid="2384677900494439426">"Pogrešna lozinka"</string>
- <string name="kg_wrong_pin" msgid="3680925703673166482">"Pogrešan PIN"</string>
- <string name="kg_pattern_instructions" msgid="8366024510502517748">"Nacrtajte šablon"</string>
- <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Unesite PIN SIM kartice"</string>
- <string name="kg_pin_instructions" msgid="7355933174673539021">"Unesite PIN"</string>
- <string name="kg_password_instructions" msgid="7179782578809398050">"Unesite lozinku"</string>
- <string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM kartica je sada onemogućena. Unesite PUK kôd da biste nastavili. Za detalje kontaktirajte operatera."</string>
- <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Unesite željeni PIN kôd"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Potvrdite željeni PIN kôd"</string>
+ <string name="display_manager_overlay_display_secure_suffix" msgid="2810034719482834679">", безбедно"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="406145459223122537">"Заборављени шаблон"</string>
+ <string name="kg_wrong_pattern" msgid="1342812634464179931">"Погрешан шаблон"</string>
+ <string name="kg_wrong_password" msgid="2384677900494439426">"Погрешна лозинка"</string>
+ <string name="kg_wrong_pin" msgid="3680925703673166482">"Погрешан PIN"</string>
+ <string name="kg_pattern_instructions" msgid="8366024510502517748">"Нацртајте шаблон"</string>
+ <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Унесите PIN SIM картице"</string>
+ <string name="kg_pin_instructions" msgid="7355933174673539021">"Унесите PIN"</string>
+ <string name="kg_password_instructions" msgid="7179782578809398050">"Унесите лозинку"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM картица је сада онемогућена. Унесите PUK кôд да бисте наставили. За детаље контактирајте оператера."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Унесите жељени PIN кôд"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Потврдите жељени PIN кôд"</string>
<!-- no translation found for kg_sim_unlock_progress_dialog_message (5743634657721110967) -->
<skip />
- <string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"PIN kôd je netačan."</string>
- <string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Unesite PIN koji ima od 4 do 8 brojeva."</string>
- <string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK kôd treba da ima 8 brojeva."</string>
- <string name="kg_invalid_puk" msgid="4809502818518963344">"Ponovo unesite ispravni PUK kôd. Ponovljeni pokušaji će trajno onemogućiti SIM."</string>
- <string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"PIN kodovi se ne podudaraju"</string>
- <string name="kg_login_too_many_attempts" msgid="699292728290654121">"Previše pokušaja unosa šablona"</string>
- <string name="kg_login_instructions" msgid="3619844310339066827">"Da biste otključali, prijavite se pomoću Google naloga."</string>
- <string name="kg_login_username_hint" msgid="1765453775467133251">"Korisničko ime (imejl adresa)"</string>
- <string name="kg_login_password_hint" msgid="3330530727273164402">"Lozinka"</string>
- <string name="kg_login_submit_button" msgid="893611277617096870">"Prijavi me"</string>
- <string name="kg_login_invalid_input" msgid="8292367491901220210">"Nevažeće korisničko ime ili lozinka."</string>
- <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Zaboravili ste korisničko ime ili lozinku?\nPosetite adresu "<b>"google.com/accounts/recovery"</b>"."</string>
- <string name="kg_login_checking_password" msgid="4676010303243317253">"Provera naloga…"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Uneli ste netačni PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunde/i."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Uneli ste netačnu lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunde/i."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Nacrtali ste šablon za otključavanje netačno <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunde/i."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Pokušali ste da otključate tablet netačno <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešna(ih) pokušaja tablet će biti resetovan na fabrička podešavanja i svi korisnički podaci će biti izgubljeni."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Broj vaših neuspešnih pokušaja da otključate Android TV uređaj: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Broj preostalih neuspešnih pokušaja posle kojih će se Android TV resetovati na fabrička podešavanja i svi podaci korisnika će biti izgubljeni: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"Pokušali ste da otključate telefon netačno <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Posle još <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešna(ih) pokušaja telefon će biti resetovan na fabrička podešavanja i svi korisnički podaci će biti izgubljeni."</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Pokušali ste da otključate tablet netačno <xliff:g id="NUMBER">%d</xliff:g> puta. Tablet će sada biti vraćen na podrazumevana fabrička podešavanja."</string>
- <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Broj vaših neuspešnih pokušaja da otključate Android TV uređaj: <xliff:g id="NUMBER">%d</xliff:g>. Android TV uređaj će se sada resetovati na fabrička podešavanja."</string>
- <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Pokušali ste da otključate telefon netačno <xliff:g id="NUMBER">%d</xliff:g> puta. Telefon će sada biti vraćen na podrazumevana fabrička podešavanja."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Nacrtali ste šablon za otključavanje netačno <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Posle još <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešna(ih) pokušaja, od vas će biti zatraženo da otključate tablet pomoću naloga e-pošte.\n\nProbajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunde/i."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Netačno ste nacrtali šablon za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još puta (<xliff:g id="NUMBER_1">%2$d</xliff:g>), zatražićemo da otključate telefon pomoću Android TV uređaja.\n\n Probajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Nacrtali ste šablon za otključavanje netačno <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Posle još <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešna(ih) pokušaja, od vas će biti zatraženo da otključate telefon pomoću naloga e-pošte.\n\nProbajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunde/i."</string>
+ <string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"PIN кôд је нетачан."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Унесите PIN који има од 4 до 8 бројева."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK кôд треба да има 8 бројева."</string>
+ <string name="kg_invalid_puk" msgid="4809502818518963344">"Поново унесите исправни PUK кôд. Поновљени покушаји ће трајно онемогућити SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"PIN кодови се не подударају"</string>
+ <string name="kg_login_too_many_attempts" msgid="699292728290654121">"Превише покушаја уноса шаблона"</string>
+ <string name="kg_login_instructions" msgid="3619844310339066827">"Да бисте откључали, пријавите се помоћу Google налога."</string>
+ <string name="kg_login_username_hint" msgid="1765453775467133251">"Корисничко име (имејл адреса)"</string>
+ <string name="kg_login_password_hint" msgid="3330530727273164402">"Лозинка"</string>
+ <string name="kg_login_submit_button" msgid="893611277617096870">"Пријави ме"</string>
+ <string name="kg_login_invalid_input" msgid="8292367491901220210">"Неважеће корисничко име или лозинка."</string>
+ <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Заборавили сте корисничко име или лозинку?\nПосетите адресу "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="4676010303243317253">"Провера налога…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Унели сте нетачни PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде/и."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Унели сте нетачну лозинку <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде/и."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунде/и."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Покушали сте да откључате таблет нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја таблет ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Број ваших неуспешних покушаја да откључате Android TV уређај: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Број преосталих неуспешних покушаја после којих ће се Android TV ресетовати на фабричка подешавања и сви подаци корисника ће бити изгубљени: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"Покушали сте да откључате телефон нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја телефон ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Покушали сте да откључате таблет нетачно <xliff:g id="NUMBER">%d</xliff:g> пута. Таблет ће сада бити враћен на подразумевана фабричка подешавања."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Број ваших неуспешних покушаја да откључате Android TV уређај: <xliff:g id="NUMBER">%d</xliff:g>. Android TV уређај ће се сада ресетовати на фабричка подешавања."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Покушали сте да откључате телефон нетачно <xliff:g id="NUMBER">%d</xliff:g> пута. Телефон ће сада бити враћен на подразумевана фабричка подешавања."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу налога е-поште.\n\nПробајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунде/и."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Нетачно сте нацртали шаблон за откључавање <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још пута (<xliff:g id="NUMBER_1">%2$d</xliff:g>), затражићемо да откључате телефон помоћу Android TV уређаја.\n\n Пробајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу налога е-поште.\n\nПробајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунде/и."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
- <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Ukloni"</string>
- <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite da pojačate zvuk iznad preporučenog nivoa?\n\nSlušanje glasne muzike duže vreme može da vam ošteti sluh."</string>
- <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li da koristite prečicu za pristupačnost?"</string>
- <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kada je prečica uključena, pritisnite oba dugmeta za jačinu zvuka da biste pokrenuli funkciju pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Želite da uključite prečicu za funkcije pristupačnosti?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ako zadržite oba tastera za jačinu zvuka par sekundi, uključiće se funkcije pristupačnosti. To može da promeni način rada uređaja.\n\nPostojeće funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nMožete da promenite izabrane funkcije u odeljku Podešavanja &gt; Pristupačnost."</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Уклони"</string>
+ <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Желите да појачате звук изнад препорученог нивоа?\n\nСлушање гласне музике дуже време може да вам оштети слух."</string>
+ <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Желите ли да користите пречицу за приступачност?"</string>
+ <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Када је пречица укључена, притисните оба дугмета за јачину звука да бисте покренули функцију приступачности."</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Желите да укључите пречицу за функције приступачности?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ако задржите оба тастера за јачину звука пар секунди, укључиће се функције приступачности. То може да промени начин рада уређаја.\n\nПостојеће функције:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМожете да промените изабране функције у одељку Подешавања &gt; Приступачност."</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">"Želite da uključite prečicu za uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako zadržite oba tastera za jačinu zvuka par sekundi, uključuje se <xliff:g id="SERVICE">%1$s</xliff:g>, funkcija pristupačnosti. To može da promeni način rada uređaja.\n\nMožete da promenite funkciju na koju se odnosi ova prečica u odeljku Podešavanja &gt; Pristupačnost."</string>
- <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
- <string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne uključuj"</string>
- <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"UKLJUČENO"</string>
- <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"ISKLJUČENO"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Želite li da dozvolite da usluga <xliff:g id="SERVICE">%1$s</xliff:g> ima potpunu kontrolu nad uređajem?"</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"Potpuna kontrola je primerena za aplikacije koje vam pomažu kod usluga pristupačnosti, ali ne i za većinu aplikacija."</string>
- <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Pregledaj i kontroliši ekran"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Može da čita sav sadržaj na ekranu i prikazuje ga u drugim aplikacijama."</string>
- <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Pregledaj i obavljaj radnje"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Može da prati interakcije sa aplikacijom ili senzorom hardvera i koristi aplikacije umesto vas."</string>
- <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Dozvoli"</string>
- <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Odbij"</string>
- <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite neku funkciju da biste počeli da je koristite:"</string>
- <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odaberite funkcije koje ćete koristiti sa dugmetom Pristupačnost"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odaberite funkcije za prečicu tasterom jačine zvuka"</string>
- <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usluga <xliff:g id="SERVICE_NAME">%s</xliff:g> je isključena"</string>
- <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Izmenite prečice"</string>
- <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gotovo"</string>
- <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Isključi prečicu"</string>
- <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
- <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Желите да укључите пречицу за услугу <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ако задржите оба тастера за јачину звука пар секунди, укључује се <xliff:g id="SERVICE">%1$s</xliff:g>, функција приступачности. То може да промени начин рада уређаја.\n\nМожете да промените функцију на коју се односи ова пречица у одељку Подешавања &gt; Приступачност."</string>
+ <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Укључи"</string>
+ <string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не укључуј"</string>
+ <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"УКЉУЧЕНО"</string>
+ <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"ИСКЉУЧЕНО"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Желите ли да дозволите да услуга <xliff:g id="SERVICE">%1$s</xliff:g> има потпуну контролу над уређајем?"</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"Потпуна контрола је примерена за апликације које вам помажу код услуга приступачности, али не и за већину апликација."</string>
+ <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Прегледај и контролиши екран"</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Може да чита сав садржај на екрану и приказује га у другим апликацијама."</string>
+ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Прегледај и обављај радње"</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Може да прати интеракције са апликацијом или сензором хардвера и користи апликације уместо вас."</string>
+ <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Дозволи"</string>
+ <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Одбиј"</string>
+ <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Додирните неку функцију да бисте почели да је користите:"</string>
+ <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Одаберите функције које ћете користити са дугметом Приступачност"</string>
+ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Одаберите функције за пречицу тастером јачине звука"</string>
+ <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Услуга <xliff:g id="SERVICE_NAME">%s</xliff:g> је искључена"</string>
+ <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Измените пречице"</string>
+ <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
+ <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Искључи пречицу"</string>
+ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи пречицу"</string>
+ <string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија боја"</string>
<!-- no translation found for color_correction_feature_name (7975133554160979214) -->
<skip />
- <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jednom rukom"</string>
- <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatno zatamnjeno"</string>
- <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
- <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite oba tastera za jačinu zvuka tri sekunde da biste koristili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Izaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Odaberite funkciju koja će se koristiti pomoću pokreta za pristupačnost (pomoću dva prsta prevucite nagore od dna ekrana):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Odaberite funkciju koja će se koristiti pomoću pokreta za pristupačnost (pomoću tri prsta prevucite nagore od dna ekrana):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Da biste prelazili sa jedne funkcije na drugu, dodirnite i zadržite dugme Pristupačnost."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Da biste prelazili sa jedne funkcije na drugu, prevucite nagore pomoću dva prsta i zadržite."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Da biste prelazili sa jedne funkcije na drugu, prevucite nagore pomoću tri prsta i zadržite."</string>
- <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Uvećanje"</string>
- <string name="user_switched" msgid="7249833311585228097">"Aktuelni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
- <string name="user_switching_message" msgid="1912993630661332336">"Prebacivanje na <xliff:g id="NAME">%1$s</xliff:g>…"</string>
- <string name="user_logging_out_message" msgid="7216437629179710359">"Odjavljuje se <xliff:g id="NAME">%1$s</xliff:g>…"</string>
- <string name="owner_name" msgid="8713560351570795743">"Vlasnik"</string>
- <string name="guest_name" msgid="8502103277839834324">"Gost"</string>
- <string name="error_message_title" msgid="4082495589294631966">"Greška"</string>
- <string name="error_message_change_not_allowed" msgid="843159705042381454">"Administrator nije dozvolio ovu promenu"</string>
- <string name="app_not_found" msgid="3429506115332341800">"Nije pronađena nijedna aplikacija koja bi mogla da obavi ovu radnju"</string>
- <string name="revoke" msgid="5526857743819590458">"Opozovi"</string>
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим једном руком"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Додатно затамњено"</string>
+ <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је укључена."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је искључена."</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притисните и задржите оба тастера за јачину звука три секунде да бисте користили <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Изаберите функцију која ће се користити када додирнете дугме Приступачност:"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Одаберите функцију која ће се користити помоћу покрета за приступачност (помоћу два прста превуците нагоре од дна екрана):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Одаберите функцију која ће се користити помоћу покрета за приступачност (помоћу три прста превуците нагоре од дна екрана):"</string>
+ <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Да бисте прелазили са једне функције на другу, додирните и задржите дугме Приступачност."</string>
+ <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Да бисте прелазили са једне функције на другу, превуците нагоре помоћу два прста и задржите."</string>
+ <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Да бисте прелазили са једне функције на другу, превуците нагоре помоћу три прста и задржите."</string>
+ <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Увећање"</string>
+ <string name="user_switched" msgid="7249833311585228097">"Актуелни корисник <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="user_switching_message" msgid="1912993630661332336">"Пребацивање на <xliff:g id="NAME">%1$s</xliff:g>…"</string>
+ <string name="user_logging_out_message" msgid="7216437629179710359">"Одјављује се <xliff:g id="NAME">%1$s</xliff:g>…"</string>
+ <string name="owner_name" msgid="8713560351570795743">"Власник"</string>
+ <string name="guest_name" msgid="8502103277839834324">"Гост"</string>
+ <string name="error_message_title" msgid="4082495589294631966">"Грешка"</string>
+ <string name="error_message_change_not_allowed" msgid="843159705042381454">"Администратор није дозволио ову промену"</string>
+ <string name="app_not_found" msgid="3429506115332341800">"Није пронађена ниједна апликација која би могла да обави ову радњу"</string>
+ <string name="revoke" msgid="5526857743819590458">"Опозови"</string>
<string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string>
<string name="mediasize_iso_a1" msgid="4063589931031977223">"ISO A1"</string>
<string name="mediasize_iso_a2" msgid="2779860175680233980">"ISO A2"</string>
@@ -1871,486 +1871,490 @@
<string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string>
<string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string>
<string name="mediasize_japanese_l" msgid="1326765321473431817">"L"</string>
- <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Nepoznata veličina, uspravno"</string>
- <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Nepoznata veličina, vodoravno"</string>
- <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Otkazano je"</string>
- <string name="write_fail_reason_cannot_write" msgid="432118118378451508">"Greška pri ispisivanju sadržaja"</string>
- <string name="reason_unknown" msgid="5599739807581133337">"nepoznato"</string>
- <string name="reason_service_unavailable" msgid="5288405248063804713">"Usluga štampanja nije omogućena"</string>
- <string name="print_service_installed_title" msgid="6134880817336942482">"Usluga <xliff:g id="NAME">%s</xliff:g> je instalirana"</string>
- <string name="print_service_installed_message" msgid="7005672469916968131">"Dodirnite da biste omogućili"</string>
- <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Unesite PIN administratora"</string>
- <string name="restr_pin_enter_pin" msgid="373139384161304555">"Unesite PIN"</string>
- <string name="restr_pin_incorrect" msgid="3861383632940852496">"Netačno"</string>
- <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Aktuelni PIN"</string>
- <string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"Novi PIN"</string>
- <string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Potvrdite novi PIN"</string>
- <string name="restr_pin_create_pin" msgid="917067613896366033">"Napravite PIN za izmenu ograničenja"</string>
- <string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"PIN-ovi se ne podudaraju. Probajte ponovo."</string>
- <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN je prekratak. Mora da sadrži najmanje 4 cifre."</string>
- <string name="restr_pin_try_later" msgid="5897719962541636727">"Probajte ponovo kasnije"</string>
- <string name="immersive_cling_title" msgid="2307034298721541791">"Prikazuje se ceo ekran"</string>
- <string name="immersive_cling_description" msgid="7092737175345204832">"Da biste izašli, prevucite nadole odozgo."</string>
- <string name="immersive_cling_positive" msgid="7047498036346489883">"Važi"</string>
- <string name="done_label" msgid="7283767013231718521">"Gotovo"</string>
- <string name="hour_picker_description" msgid="5153757582093524635">"Kružni klizač za sate"</string>
- <string name="minute_picker_description" msgid="9029797023621927294">"Kružni klizač za minute"</string>
- <string name="select_hours" msgid="5982889657313147347">"Izaberite sate"</string>
- <string name="select_minutes" msgid="9157401137441014032">"Izaberite minute"</string>
- <string name="select_day" msgid="2060371240117403147">"Izaberite mesec i dan"</string>
- <string name="select_year" msgid="1868350712095595393">"Izaberite godinu"</string>
- <string name="deleted_key" msgid="9130083334943364001">"Izbrisali ste <xliff:g id="KEY">%1$s</xliff:g>"</string>
- <string name="managed_profile_label_badge" msgid="6762559569999499495">"<xliff:g id="LABEL">%1$s</xliff:g> na poslu"</string>
- <string name="managed_profile_label_badge_2" msgid="5673187309555352550">"2. poslovni <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="managed_profile_label_badge_3" msgid="6882151970556391957">"3. poslovni imejl <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Traži PIN pre otkačinjanja"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Traži šablon za otključavanje pre otkačinjanja"</string>
- <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Traži lozinku pre otkačinjanja"</string>
- <string name="package_installed_device_owner" msgid="7035926868974878525">"Instalirao je administrator"</string>
- <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je administrator"</string>
- <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je administrator"</string>
- <string name="confirm_battery_saver" msgid="5247976246208245754">"Potvrdi"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte, određene funkcije i neke mrežne veze."</string>
- <string name="battery_saver_description" msgid="8518809702138617167">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte, određene funkcije i neke mrežne veze."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjila potrošnja podataka, Ušteda podataka sprečava neke aplikacije da šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može da pristupa podacima, ali će to činiti ređe. Na primer, slike se neće prikazivati dok ih ne dodirnete."</string>
- <string name="data_saver_enable_title" msgid="7080620065745260137">"Želite da uključite Uštedu podataka?"</string>
- <string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string>
- <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Jedan minut (do {formattedTime})}one{# minut (do {formattedTime})}few{# minuta (do {formattedTime})}other{# minuta (do {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{1 min (do {formattedTime})}one{# min (do {formattedTime})}few{# min (do {formattedTime})}other{# min (do {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{1 sat (do {formattedTime})}one{# sat (do {formattedTime})}few{# sata (do {formattedTime})}other{# sati (do {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{1 s (do {formattedTime})}one{# s (do {formattedTime})}few{# s (do {formattedTime})}other{# s (do {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Jedan minut}one{# minut}few{# minuta}other{# minuta}}"</string>
- <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{1 min}one{# min}few{# min}other{# min}}"</string>
- <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{1 sat}one{# sat}few{# sata}other{# sati}}"</string>
- <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{1 s}one{# s}few{# s}other{# s}}"</string>
- <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
- <string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
- <string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sledeći alarm)"</string>
- <string name="zen_mode_forever" msgid="740585666364912448">"Dok ne isključite"</string>
- <string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Dok ne isključite režim Ne uznemiravaj"</string>
+ <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Непозната величина, усправно"</string>
+ <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Непозната величина, водоравно"</string>
+ <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Отказано је"</string>
+ <string name="write_fail_reason_cannot_write" msgid="432118118378451508">"Грешка при исписивању садржаја"</string>
+ <string name="reason_unknown" msgid="5599739807581133337">"непознато"</string>
+ <string name="reason_service_unavailable" msgid="5288405248063804713">"Услуга штампања није омогућена"</string>
+ <string name="print_service_installed_title" msgid="6134880817336942482">"Услуга <xliff:g id="NAME">%s</xliff:g> је инсталирана"</string>
+ <string name="print_service_installed_message" msgid="7005672469916968131">"Додирните да бисте омогућили"</string>
+ <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Унесите PIN администратора"</string>
+ <string name="restr_pin_enter_pin" msgid="373139384161304555">"Унесите PIN"</string>
+ <string name="restr_pin_incorrect" msgid="3861383632940852496">"Нетачно"</string>
+ <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Актуелни PIN"</string>
+ <string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"Нови PIN"</string>
+ <string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Потврдите нови PIN"</string>
+ <string name="restr_pin_create_pin" msgid="917067613896366033">"Направите PIN за измену ограничења"</string>
+ <string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"PIN-ови се не подударају. Пробајте поново."</string>
+ <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN је прекратак. Мора да садржи најмање 4 цифре."</string>
+ <string name="restr_pin_try_later" msgid="5897719962541636727">"Пробајте поново касније"</string>
+ <string name="immersive_cling_title" msgid="2307034298721541791">"Приказује се цео екран"</string>
+ <string name="immersive_cling_description" msgid="7092737175345204832">"Да бисте изашли, превуците надоле одозго."</string>
+ <string name="immersive_cling_positive" msgid="7047498036346489883">"Важи"</string>
+ <string name="done_label" msgid="7283767013231718521">"Готово"</string>
+ <string name="hour_picker_description" msgid="5153757582093524635">"Кружни клизач за сате"</string>
+ <string name="minute_picker_description" msgid="9029797023621927294">"Кружни клизач за минуте"</string>
+ <string name="select_hours" msgid="5982889657313147347">"Изаберите сате"</string>
+ <string name="select_minutes" msgid="9157401137441014032">"Изаберите минуте"</string>
+ <string name="select_day" msgid="2060371240117403147">"Изаберите месец и дан"</string>
+ <string name="select_year" msgid="1868350712095595393">"Изаберите годину"</string>
+ <string name="deleted_key" msgid="9130083334943364001">"Избрисали сте <xliff:g id="KEY">%1$s</xliff:g>"</string>
+ <string name="managed_profile_label_badge" msgid="6762559569999499495">"<xliff:g id="LABEL">%1$s</xliff:g> на послу"</string>
+ <string name="managed_profile_label_badge_2" msgid="5673187309555352550">"2. пословни <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="managed_profile_label_badge_3" msgid="6882151970556391957">"3. пословни имејл <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Тражи PIN пре откачињања"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Тражи шаблон за откључавање пре откачињања"</string>
+ <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Тражи лозинку пре откачињања"</string>
+ <string name="package_installed_device_owner" msgid="7035926868974878525">"Инсталирао је администратор"</string>
+ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирао је администратор"</string>
+ <string name="package_deleted_device_owner" msgid="2292335928930293023">"Избрисао је администратор"</string>
+ <string name="confirm_battery_saver" msgid="5247976246208245754">"Потврди"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Уштеда батерије укључује тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте, одређене функције и неке мрежне везе."</string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Уштеда батерије укључује тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте, одређене функције и неке мрежне везе."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Да би се смањила потрошња података, Уштеда података спречава неке апликације да шаљу или примају податке у позадини. Апликација коју тренутно користите може да приступа подацима, али ће то чинити ређе. На пример, слике се неће приказивати док их не додирнете."</string>
+ <string name="data_saver_enable_title" msgid="7080620065745260137">"Желите да укључите Уштеду података?"</string>
+ <string name="data_saver_enable_button" msgid="4399405762586419726">"Укључи"</string>
+ <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Један минут (до {formattedTime})}one{# минут (до {formattedTime})}few{# минута (до {formattedTime})}other{# минута (до {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{1 мин (до {formattedTime})}one{# мин (до {formattedTime})}few{# мин (до {formattedTime})}other{# мин (до {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{1 сат (до {formattedTime})}one{# сат (до {formattedTime})}few{# сата (до {formattedTime})}other{# сати (до {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{1 с (до {formattedTime})}one{# с (до {formattedTime})}few{# с (до {formattedTime})}other{# с (до {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Један минут}one{# минут}few{# минута}other{# минута}}"</string>
+ <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{1 мин}one{# мин}few{# мин}other{# мин}}"</string>
+ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{1 сат}one{# сат}few{# сата}other{# сати}}"</string>
+ <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{1 с}one{# с}few{# с}other{# с}}"</string>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_alarm" msgid="7046911727540499275">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (следећи аларм)"</string>
+ <string name="zen_mode_forever" msgid="740585666364912448">"Док не искључите"</string>
+ <string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Док не искључите режим Не узнемиравај"</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">"Skupi"</string>
- <string name="zen_mode_feature_name" msgid="3785547207263754500">"Ne uznemiravaj"</string>
- <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"Odmor"</string>
- <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Radni dan uveče"</string>
- <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
- <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
- <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvuke"</string>
- <string name="system_error_wipe_data" msgid="5910572292172208493">"Došlo je do internog problema u vezi sa uređajem i možda će biti nestabilan dok ne obavite resetovanje na fabrička podešavanja."</string>
- <string name="system_error_manufacturer" msgid="703545241070116315">"Došlo je do internog problema u vezi sa uređajem. Potražite detalje od proizvođača."</string>
- <string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD zahtev je promenjen u običan poziv"</string>
- <string name="stk_cc_ussd_to_ss" msgid="4826846653052609738">"USSD zahtev je promenjen u SS zahtev"</string>
- <string name="stk_cc_ussd_to_ussd" msgid="8343001461299302472">"Promenjeno je u novi USSD zahtev"</string>
- <string name="stk_cc_ussd_to_dial_video" msgid="429118590323618623">"USSD zahtev je promenjen u video poziv"</string>
- <string name="stk_cc_ss_to_dial" msgid="4087396658768717077">"SS zahtev je promenjen u običan poziv"</string>
- <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS zahtev je promenjen u video poziv"</string>
- <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS zahtev je promenjen u USSD zahtev"</string>
- <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Promenjeno je u novi SS zahtev"</string>
- <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Upozorenje o „pecanju“"</string>
- <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Poslovni profil"</string>
- <string name="notification_alerted_content_description" msgid="6139691253611265992">"Obavešteno"</string>
- <string name="notification_verified_content_description" msgid="6401483602782359391">"Verifikovano"</string>
- <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Proširi"</string>
- <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Skupi"</string>
- <string name="expand_action_accessibility" msgid="1947657036871746627">"uključite/isključite proširenje"</string>
- <string name="usb_midi_peripheral_name" msgid="490523464968655741">"Android USB port za periferijske uređaje"</string>
+ <string name="toolbar_collapse_description" msgid="8009920446193610996">"Скупи"</string>
+ <string name="zen_mode_feature_name" msgid="3785547207263754500">"Не узнемиравај"</string>
+ <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"Одмор"</string>
+ <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Радни дан увече"</string>
+ <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Викенд"</string>
+ <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Догађај"</string>
+ <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Спавање"</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> искључује неке звуке"</string>
+ <string name="system_error_wipe_data" msgid="5910572292172208493">"Дошло је до интерног проблема у вези са уређајем и можда ће бити нестабилан док не обавите ресетовање на фабричка подешавања."</string>
+ <string name="system_error_manufacturer" msgid="703545241070116315">"Дошло је до интерног проблема у вези са уређајем. Потражите детаље од произвођача."</string>
+ <string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD захтев је промењен у обичан позив"</string>
+ <string name="stk_cc_ussd_to_ss" msgid="4826846653052609738">"USSD захтев је промењен у SS захтев"</string>
+ <string name="stk_cc_ussd_to_ussd" msgid="8343001461299302472">"Промењено је у нови USSD захтев"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="429118590323618623">"USSD захтев је промењен у видео позив"</string>
+ <string name="stk_cc_ss_to_dial" msgid="4087396658768717077">"SS захтев је промењен у обичан позив"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS захтев је промењен у видео позив"</string>
+ <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS захтев је промењен у USSD захтев"</string>
+ <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Промењено је у нови SS захтев"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Упозорење о „пецању“"</string>
+ <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Пословни профил"</string>
+ <string name="notification_alerted_content_description" msgid="6139691253611265992">"Обавештено"</string>
+ <string name="notification_verified_content_description" msgid="6401483602782359391">"Верификовано"</string>
+ <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Прошири"</string>
+ <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Скупи"</string>
+ <string name="expand_action_accessibility" msgid="1947657036871746627">"укључите/искључите проширење"</string>
+ <string name="usb_midi_peripheral_name" msgid="490523464968655741">"Android USB порт за периферијске уређаје"</string>
<string name="usb_midi_peripheral_manufacturer_name" msgid="7557148557088787741">"Android"</string>
- <string name="usb_midi_peripheral_product_name" msgid="2836276258480904434">"USB port za periferijske uređaje"</string>
- <string name="floating_toolbar_open_overflow_description" msgid="2260297653578167367">"Još opcija"</string>
- <string name="floating_toolbar_close_overflow_description" msgid="3949818077708138098">"Zatvori preklopni meni"</string>
- <string name="maximize_button_text" msgid="4258922519914732645">"Uvećaj"</string>
- <string name="close_button_text" msgid="10603510034455258">"Zatvori"</string>
+ <string name="usb_midi_peripheral_product_name" msgid="2836276258480904434">"USB порт за периферијске уређаје"</string>
+ <string name="floating_toolbar_open_overflow_description" msgid="2260297653578167367">"Још опција"</string>
+ <string name="floating_toolbar_close_overflow_description" msgid="3949818077708138098">"Затвори преклопни мени"</string>
+ <string name="maximize_button_text" msgid="4258922519914732645">"Увећај"</string>
+ <string name="close_button_text" msgid="10603510034455258">"Затвори"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
- <string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string>
- <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
- <string name="call_notification_decline_action" msgid="3700345945214000726">"Odbij"</string>
- <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini vezu"</string>
- <string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string>
- <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv je u toku"</string>
- <string name="call_notification_screening_text" msgid="8396931408268940208">"Proverava se dolazni poziv"</string>
- <string name="default_notification_channel_label" msgid="3697928973567217330">"Nekategorizovano"</string>
- <string name="importance_from_user" msgid="2782756722448800447">"Vi podešavate važnost ovih obaveštenja."</string>
- <string name="importance_from_person" msgid="4235804979664465383">"Ovo je važno zbog ljudi koji učestvuju."</string>
- <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Prilagođeno obaveštenje o aplikaciji"</string>
- <string name="user_creation_account_exists" msgid="2239146360099708035">"Želite li da dozvolite da <xliff:g id="APP">%1$s</xliff:g> napravi novog korisnika sa nalogom <xliff:g id="ACCOUNT">%2$s</xliff:g> (korisnik sa tim nalogom već postoji)?"</string>
- <string name="user_creation_adding" msgid="7305185499667958364">"Želite li da dozvolite da <xliff:g id="APP">%1$s</xliff:g> napravi novog korisnika sa nalogom <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="supervised_user_creation_label" msgid="6884904353827427515">"Dodajte korisnika pod nadzorom"</string>
- <string name="language_selection_title" msgid="52674936078683285">"Dodajte jezik"</string>
- <string name="country_selection_title" msgid="5221495687299014379">"Podešavanje regiona"</string>
- <string name="search_language_hint" msgid="7004225294308793583">"Unesite naziv jezika"</string>
- <string name="language_picker_section_suggested" msgid="6556199184638990447">"Predloženi"</string>
- <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Predloženo"</string>
- <string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Predloženi jezici"</string>
- <string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Predloženi regioni"</string>
- <string name="language_picker_section_all" msgid="1985809075777564284">"Svi jezici"</string>
- <string name="region_picker_section_all" msgid="756441309928774155">"Svi regioni"</string>
- <string name="locale_search_menu" msgid="6258090710176422934">"Pretraži"</string>
- <string name="app_suspended_title" msgid="888873445010322650">"Aplikacija nije dostupna"</string>
- <string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> trenutno nije dostupna. <xliff:g id="APP_NAME_1">%2$s</xliff:g> upravlja dostupnošću."</string>
- <string name="app_suspended_more_details" msgid="211260942831587014">"Saznajte više"</string>
- <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Opozovi pauziranje aplikacije"</string>
- <string name="work_mode_off_title" msgid="961171256005852058">"Uključujete poslovne aplikacije?"</string>
- <string name="work_mode_off_message" msgid="7319580997683623309">"Pristupajte poslovnim aplikacijama i obaveštenjima"</string>
- <string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
- <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
- <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
- <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – nije dostupno"</string>
- <string name="app_streaming_blocked_title_for_permission_dialog" msgid="4483161748582966785">"Potrebna je dozvola"</string>
- <string name="app_streaming_blocked_title_for_camera_dialog" msgid="3935701653713853065">"Kamera nije dostupna"</string>
- <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Nastavite na telefonu"</string>
- <string name="app_streaming_blocked_title_for_microphone_dialog" msgid="544822455127171206">"Mikrofon je nedostupan"</string>
- <string name="app_streaming_blocked_title_for_playstore_dialog" msgid="8149823099822897538">"Play prodavnica nije dostupna"</string>
- <string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"Podešavanja Android TV-a su nedostupna"</string>
- <string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"Podešavanja tableta su nedostupna"</string>
- <string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"Podešavanja telefona su nedostupna"</string>
- <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Ovoj aplikaciji trenutno ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na Android TV uređaju."</string>
- <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Ovoj aplikaciji trenutno ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na tabletu."</string>
- <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Ovoj aplikaciji trenutno ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na telefonu."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Ova aplikacija zahteva dodatnu bezbednost. Probajte na Android TV uređaju."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Ova aplikacija zahteva dodatnu bezbednost. Probajte na tabletu."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Ova aplikacija zahteva dodatnu bezbednost. Probajte na telefonu."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Ovoj aplikaciji ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na Android TV uređaju."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Ovoj aplikaciji ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na tabletu."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ovoj aplikaciji ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na telefonu."</string>
- <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ova aplikacija je napravljena za stariju verziju Android-a. Možda neće raditi ispravno i ne obuhvata najnovije bezbednosne funkcije i zaštite privatnosti. Proverite da li ima ažuriranja ili se obratite programeru aplikacije."</string>
- <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Potraži ažuriranje"</string>
- <string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string>
- <string name="new_sms_notification_content" msgid="3197949934153460639">"Otvorite aplikaciju za SMS da biste pregledali"</string>
- <string name="profile_encrypted_title" msgid="9001208667521266472">"Neke funkcije su možda ograničene"</string>
- <string name="profile_encrypted_detail" msgid="5279730442756849055">"Poslovni profil je zaključan"</string>
- <string name="profile_encrypted_message" msgid="1128512616293157802">"Dodirom otklj. poslovni profil"</string>
- <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Povezano je sa proizvodom <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
- <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Dodirnite za pregled datoteka"</string>
- <string name="pin_target" msgid="8036028973110156895">"Zakači"</string>
- <string name="pin_specific_target" msgid="7824671240625957415">"Zakači aplikaciju <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="unpin_target" msgid="3963318576590204447">"Otkači"</string>
- <string name="unpin_specific_target" msgid="3859828252160908146">"Otkači aplikaciju <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="app_info" msgid="6113278084877079851">"Informacije o aplikaciji"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Одговори"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Видео"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Одбиј"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Прекини везу"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Долазни позив"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Позив је у току"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Проверава се долазни позив"</string>
+ <string name="default_notification_channel_label" msgid="3697928973567217330">"Некатегоризовано"</string>
+ <string name="importance_from_user" msgid="2782756722448800447">"Ви подешавате важност ових обавештења."</string>
+ <string name="importance_from_person" msgid="4235804979664465383">"Ово је важно због људи који учествују."</string>
+ <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Прилагођено обавештење о апликацији"</string>
+ <string name="user_creation_account_exists" msgid="2239146360099708035">"Желите ли да дозволите да <xliff:g id="APP">%1$s</xliff:g> направи новог корисника са налогом <xliff:g id="ACCOUNT">%2$s</xliff:g> (корисник са тим налогом већ постоји)?"</string>
+ <string name="user_creation_adding" msgid="7305185499667958364">"Желите ли да дозволите да <xliff:g id="APP">%1$s</xliff:g> направи новог корисника са налогом <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
+ <string name="supervised_user_creation_label" msgid="6884904353827427515">"Додајте корисника под надзором"</string>
+ <string name="language_selection_title" msgid="52674936078683285">"Додајте језик"</string>
+ <string name="country_selection_title" msgid="5221495687299014379">"Подешавање региона"</string>
+ <string name="search_language_hint" msgid="7004225294308793583">"Унесите назив језика"</string>
+ <string name="language_picker_section_suggested" msgid="6556199184638990447">"Предложени"</string>
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Предложено"</string>
+ <string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Предложени језици"</string>
+ <string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Предложени региони"</string>
+ <string name="language_picker_section_all" msgid="1985809075777564284">"Сви језици"</string>
+ <string name="region_picker_section_all" msgid="756441309928774155">"Сви региони"</string>
+ <string name="locale_search_menu" msgid="6258090710176422934">"Претражи"</string>
+ <string name="app_suspended_title" msgid="888873445010322650">"Апликација није доступна"</string>
+ <string name="app_suspended_default_message" msgid="6451215678552004172">"Апликација <xliff:g id="APP_NAME_0">%1$s</xliff:g> тренутно није доступна. <xliff:g id="APP_NAME_1">%2$s</xliff:g> управља доступношћу."</string>
+ <string name="app_suspended_more_details" msgid="211260942831587014">"Сазнајте више"</string>
+ <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Опозови паузирање апликације"</string>
+ <string name="work_mode_off_title" msgid="961171256005852058">"Укључујете пословне апликације?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Приступајте пословним апликацијама и обавештењима"</string>
+ <string name="work_mode_turn_on" msgid="3662561662475962285">"Укључи"</string>
+ <string name="app_blocked_title" msgid="7353262160455028160">"Апликација није доступна"</string>
+ <string name="app_blocked_message" msgid="542972921087873023">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> тренутно није доступна."</string>
+ <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – није доступно"</string>
+ <string name="app_streaming_blocked_title_for_permission_dialog" msgid="4483161748582966785">"Потребна је дозвола"</string>
+ <string name="app_streaming_blocked_title_for_camera_dialog" msgid="3935701653713853065">"Камера није доступна"</string>
+ <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Наставите на телефону"</string>
+ <string name="app_streaming_blocked_title_for_microphone_dialog" msgid="544822455127171206">"Микрофон је недоступан"</string>
+ <string name="app_streaming_blocked_title_for_playstore_dialog" msgid="8149823099822897538">"Play продавница није доступна"</string>
+ <string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"Подешавања Android TV-а су недоступна"</string>
+ <string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"Подешавања таблета су недоступна"</string>
+ <string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"Подешавања телефона су недоступна"</string>
+ <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Овој апликацији тренутно не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на Android TV уређају."</string>
+ <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Овој апликацији тренутно не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на таблету."</string>
+ <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Овој апликацији тренутно не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на телефону."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Ова апликација захтева додатну безбедност. Пробајте на Android TV уређају."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Ова апликација захтева додатну безбедност. Пробајте на таблету."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Ова апликација захтева додатну безбедност. Пробајте на телефону."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Овој апликацији не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на Android TV уређају."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Овој апликацији не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на таблету."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Овој апликацији не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на телефону."</string>
+ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ова апликација је направљена за старију верзију Android-а. Можда неће радити исправно и не обухвата најновије безбедносне функције и заштите приватности. Проверите да ли има ажурирања или се обратите програмеру апликације."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Потражи ажурирање"</string>
+ <string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нове поруке"</string>
+ <string name="new_sms_notification_content" msgid="3197949934153460639">"Отворите апликацију за SMS да бисте прегледали"</string>
+ <string name="profile_encrypted_title" msgid="9001208667521266472">"Неке функције су можда ограничене"</string>
+ <string name="profile_encrypted_detail" msgid="5279730442756849055">"Пословни профил је закључан"</string>
+ <string name="profile_encrypted_message" msgid="1128512616293157802">"Додиром откљ. пословни профил"</string>
+ <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Повезано је са производом <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
+ <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Додирните за преглед датотека"</string>
+ <string name="pin_target" msgid="8036028973110156895">"Закачи"</string>
+ <string name="pin_specific_target" msgid="7824671240625957415">"Закачи апликацију <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="unpin_target" msgid="3963318576590204447">"Откачи"</string>
+ <string name="unpin_specific_target" msgid="3859828252160908146">"Откачи апликацију <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="app_info" msgid="6113278084877079851">"Информације о апликацији"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="demo_starting_message" msgid="6577581216125805905">"Pokrećemo demonstraciju..."</string>
- <string name="demo_restarting_message" msgid="1160053183701746766">"Resetujemo uređaj..."</string>
- <string name="suspended_widget_accessibility" msgid="6331451091851326101">"Vidžet <xliff:g id="LABEL">%1$s</xliff:g> je onemogućen"</string>
- <string name="conference_call" msgid="5731633152336490471">"Konferencijski poziv"</string>
- <string name="tooltip_popup_title" msgid="7863719020269945722">"Objašnjenje"</string>
- <string name="app_category_game" msgid="4534216074910244790">"Igre"</string>
- <string name="app_category_audio" msgid="8296029904794676222">"Muzika i audio"</string>
- <string name="app_category_video" msgid="2590183854839565814">"Filmovi i video"</string>
- <string name="app_category_image" msgid="7307840291864213007">"Slike"</string>
- <string name="app_category_social" msgid="2278269325488344054">"Društvene mreže i komunikacija"</string>
- <string name="app_category_news" msgid="1172762719574964544">"Novosti i časopisi"</string>
- <string name="app_category_maps" msgid="6395725487922533156">"Mape i navigacija"</string>
- <string name="app_category_productivity" msgid="1844422703029557883">"Produktivnost"</string>
- <string name="app_category_accessibility" msgid="6643521607848547683">"Pristupačnost"</string>
- <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Memorijski prostor uređaja"</string>
- <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Otklanjanje grešaka sa USB-a"</string>
- <string name="time_picker_hour_label" msgid="4208590187662336864">"sat"</string>
- <string name="time_picker_minute_label" msgid="8307452311269824553">"minut"</string>
- <string name="time_picker_header_text" msgid="9073802285051516688">"Podesite vreme"</string>
- <string name="time_picker_input_error" msgid="8386271930742451034">"Unesite važeće vreme"</string>
- <string name="time_picker_prompt_label" msgid="303588544656363889">"Unesite vreme"</string>
- <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Pređite u režim unosa teksta radi unosa vremena."</string>
- <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Pređite u režim sata radi unosa vremena."</string>
- <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Opcije automatskog popunjavanja"</string>
- <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Sačuvajte za automatsko popunjavanje"</string>
- <string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Sadržaj ne može automatski da se popuni"</string>
- <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Nema automatski popunjenih predloga"</string>
- <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Jedan automatski popunjen predlog}one{# automatski popunjen predlog}few{# automatski popunjena predloga}other{# automatski popunjenih predloga}}"</string>
- <string name="autofill_save_title" msgid="7719802414283739775">"Želite li da sačuvate u usluzi "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
- <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Želite li da sačuvate stavku <xliff:g id="TYPE">%1$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
- <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Želite li da sačuvate stavke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"Želite li da sačuvate stavke <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> u usluzi "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title" msgid="3630695947047069136">"Želite li da ažurirate u usluzi "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_type" msgid="5264152633488495704">"Želite li da ažurirate stavku <xliff:g id="TYPE">%1$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Želite li da ažurirate stavke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Želite li da ažurirate ove stavke u usluzi "<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">"Sačuvaj"</string>
- <string name="autofill_save_no" msgid="9212826374207023544">"Ne, hvala"</string>
- <string name="autofill_save_notnow" msgid="2853932672029024195">"Ne sada"</string>
- <string name="autofill_save_never" msgid="6821841919831402526">"Nikada"</string>
- <string name="autofill_update_yes" msgid="4608662968996874445">"Ažuriraj"</string>
- <string name="autofill_continue_yes" msgid="7914985605534510385">"Nastavi"</string>
- <string name="autofill_save_type_password" msgid="5624528786144539944">"lozinka"</string>
- <string name="autofill_save_type_address" msgid="3111006395818252885">"adresa"</string>
- <string name="autofill_save_type_credit_card" msgid="3583795235862046693">"kreditna kartica"</string>
- <string name="autofill_save_type_debit_card" msgid="3169397504133097468">"debitna kartica"</string>
- <string name="autofill_save_type_payment_card" msgid="6555012156728690856">"platna kartica"</string>
- <string name="autofill_save_type_generic_card" msgid="1019367283921448608">"kartica"</string>
- <string name="autofill_save_type_username" msgid="1018816929884640882">"korisničko ime"</string>
- <string name="autofill_save_type_email_address" msgid="1303262336895591924">"imejl adresa"</string>
- <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"Ostanite mirni i potražite sklonište u okolini."</string>
- <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"Odmah se sklonite iz priobalnih regiona i oblasti pored reka na neko bezbednije mesto, na primer, na neko uzvišenje."</string>
- <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"Ostanite mirni i potražite sklonište u okolini."</string>
- <string name="etws_primary_default_message_test" msgid="4583367373909549421">"Testiranje poruka u hitnim slučajevima"</string>
- <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"Odgovori"</string>
+ <string name="demo_starting_message" msgid="6577581216125805905">"Покрећемо демонстрацију..."</string>
+ <string name="demo_restarting_message" msgid="1160053183701746766">"Ресетујемо уређај..."</string>
+ <string name="suspended_widget_accessibility" msgid="6331451091851326101">"Виџет <xliff:g id="LABEL">%1$s</xliff:g> је онемогућен"</string>
+ <string name="conference_call" msgid="5731633152336490471">"Конференцијски позив"</string>
+ <string name="tooltip_popup_title" msgid="7863719020269945722">"Објашњење"</string>
+ <string name="app_category_game" msgid="4534216074910244790">"Игре"</string>
+ <string name="app_category_audio" msgid="8296029904794676222">"Музика и аудио"</string>
+ <string name="app_category_video" msgid="2590183854839565814">"Филмови и видео"</string>
+ <string name="app_category_image" msgid="7307840291864213007">"Слике"</string>
+ <string name="app_category_social" msgid="2278269325488344054">"Друштвене мреже и комуникација"</string>
+ <string name="app_category_news" msgid="1172762719574964544">"Новости и часописи"</string>
+ <string name="app_category_maps" msgid="6395725487922533156">"Мапе и навигација"</string>
+ <string name="app_category_productivity" msgid="1844422703029557883">"Продуктивност"</string>
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Приступачност"</string>
+ <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Меморијски простор уређаја"</string>
+ <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Отклањање грешака са USB-а"</string>
+ <string name="time_picker_hour_label" msgid="4208590187662336864">"сат"</string>
+ <string name="time_picker_minute_label" msgid="8307452311269824553">"минут"</string>
+ <string name="time_picker_header_text" msgid="9073802285051516688">"Подесите време"</string>
+ <string name="time_picker_input_error" msgid="8386271930742451034">"Унесите важеће време"</string>
+ <string name="time_picker_prompt_label" msgid="303588544656363889">"Унесите време"</string>
+ <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Пређите у режим уноса текста ради уноса времена."</string>
+ <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Пређите у режим сата ради уноса времена."</string>
+ <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Опције аутоматског попуњавања"</string>
+ <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Сачувајте за аутоматско попуњавање"</string>
+ <string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Садржај не може аутоматски да се попуни"</string>
+ <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Нема аутоматски попуњених предлога"</string>
+ <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Један аутоматски попуњен предлог}one{# аутоматски попуњен предлог}few{# аутоматски попуњена предлога}other{# аутоматски попуњених предлога}}"</string>
+ <string name="autofill_save_title" msgid="7719802414283739775">"Желите ли да сачувате у услузи "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Желите ли да сачувате ставку <xliff:g id="TYPE">%1$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Желите ли да сачувате ставке <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"Желите ли да сачувате ставке <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_update_title" msgid="3630695947047069136">"Желите ли да ажурирате у услузи "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_update_title_with_type" msgid="5264152633488495704">"Желите ли да ажурирате ставку <xliff:g id="TYPE">%1$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Желите ли да ажурирате ставке <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Желите ли да ажурирате ове ставке у услузи "<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> и <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <string name="autofill_save_yes" msgid="8035743017382012850">"Сачувај"</string>
+ <string name="autofill_save_no" msgid="9212826374207023544">"Не, хвала"</string>
+ <string name="autofill_save_notnow" msgid="2853932672029024195">"Не сада"</string>
+ <string name="autofill_save_never" msgid="6821841919831402526">"Никада"</string>
+ <string name="autofill_update_yes" msgid="4608662968996874445">"Ажурирај"</string>
+ <string name="autofill_continue_yes" msgid="7914985605534510385">"Настави"</string>
+ <string name="autofill_save_type_password" msgid="5624528786144539944">"лозинка"</string>
+ <string name="autofill_save_type_address" msgid="3111006395818252885">"адреса"</string>
+ <string name="autofill_save_type_credit_card" msgid="3583795235862046693">"кредитна картица"</string>
+ <string name="autofill_save_type_debit_card" msgid="3169397504133097468">"дебитна картица"</string>
+ <string name="autofill_save_type_payment_card" msgid="6555012156728690856">"платна картица"</string>
+ <string name="autofill_save_type_generic_card" msgid="1019367283921448608">"картица"</string>
+ <string name="autofill_save_type_username" msgid="1018816929884640882">"корисничко име"</string>
+ <string name="autofill_save_type_email_address" msgid="1303262336895591924">"имејл адреса"</string>
+ <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"Останите мирни и потражите склониште у околини."</string>
+ <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"Одмах се склоните из приобалних региона и области поред река на неко безбедније место, на пример, на неко узвишење."</string>
+ <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"Останите мирни и потражите склониште у околини."</string>
+ <string name="etws_primary_default_message_test" msgid="4583367373909549421">"Тестирање порука у хитним случајевима"</string>
+ <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"Одговори"</string>
<string name="etws_primary_default_message_others" msgid="7958161706019130739"></string>
- <string name="mmcc_authentication_reject" msgid="4891965994643876369">"SIM kartica nije prilagođena za glasovne usluge"</string>
- <string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"SIM kartica nije podešena za glasovne usluge"</string>
- <string name="mmcc_illegal_ms" msgid="7509650265233909445">"SIM kartica nije prilagođena za glasovne usluge"</string>
- <string name="mmcc_illegal_me" msgid="6505557881889904915">"Telefon nije prilagođen za glasovne usluge"</string>
- <string name="mmcc_authentication_reject_msim_template" msgid="4480853038909922153">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> nije dozvoljen"</string>
- <string name="mmcc_imsi_unknown_in_hlr_msim_template" msgid="3688508325248599657">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> nije podešen"</string>
- <string name="mmcc_illegal_ms_msim_template" msgid="832644375774599327">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> nije dozvoljen"</string>
- <string name="mmcc_illegal_me_msim_template" msgid="4802735138861422802">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> nije dozvoljen"</string>
- <string name="popup_window_default_title" msgid="6907717596694826919">"Iskačući prozor"</string>
- <string name="slice_more_content" msgid="3377367737876888459">"i još <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"Aplikacija je vraćena na stariju verziju ili nije kompatibilna sa ovom prečicom"</string>
- <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Vraćanje prečice nije uspelo jer aplikacija ne podržava pravljenje rezervne kopije i vraćanje"</string>
- <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Vraćanje prečice nije uspelo jer se potpisi aplikacija ne podudaraju"</string>
- <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Vraćanje prečice nije uspelo"</string>
- <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Prečica je onemogućena"</string>
- <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"DEINSTALIRAJ"</string>
- <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"IPAK OTVORI"</string>
- <string name="harmful_app_warning_title" msgid="8794823880881113856">"Otkrivena je štetna aplikacija"</string>
- <string name="slices_permission_request" msgid="3677129866636153406">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi da prikazuje isečke iz aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string>
- <string name="screenshot_edit" msgid="7408934887203689207">"Izmeni"</string>
- <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Vibracija za pozive i obaveštenja je uključena"</string>
- <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Melodija zvona za pozive i obaveštenje je isključena"</string>
- <string name="notification_channel_system_changes" msgid="2462010596920209678">"Sistemske promene"</string>
- <string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"Ne uznemiravaj"</string>
- <string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"Novo: Režim Ne uznemiravaj krije obaveštenja"</string>
- <string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"Dodirnite da biste saznali više i promenili podešavanje."</string>
- <string name="zen_upgrade_notification_title" msgid="8198167698095298717">"Režim Ne uznemiravaj je promenjen"</string>
- <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"Dodirnite da biste proverili šta je blokirano."</string>
- <string name="review_notification_settings_title" msgid="5102557424459810820">"Pregledajte podešavanja obaveštenja"</string>
- <string name="review_notification_settings_text" msgid="5916244866751849279">"Od Android-a 13 aplikacije koje instalirate moraju da imaju dozvolu za slanje obaveštenja. Dodirnite da biste promenili ovu dozvolu za postojeće aplikacije."</string>
- <string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"Podseti me kasnije"</string>
- <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Odbaci"</string>
- <string name="notification_app_name_system" msgid="3045196791746735601">"Sistem"</string>
- <string name="notification_app_name_settings" msgid="9088548800899952531">"Podešavanja"</string>
- <string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
- <string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
- <string name="notification_appops_overlay_active" msgid="5571732753262836481">"prikazuje se na ekranu dok koristite druge aplikacije"</string>
- <string name="notification_feedback_indicator" msgid="663476517711323016">"Pošaljite povratne informacije"</string>
- <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Ovo obaveštenje je unapređeno u Podrazumevano. Dodirnite da biste naveli povratne informacije."</string>
- <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Ovo obaveštenje je degradirano u Nečujno. Dodirnite da biste naveli povratne informacije."</string>
- <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Ovo obaveštenje je rangirano više. Dodirnite da biste naveli povratne informacije."</string>
- <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Ovo obaveštenje je rangirano niže. Dodirnite da biste naveli povratne informacije."</string>
- <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Poboljšana obaveštenja"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Predložene radnje i odgovore sada dobijate pomoću poboljšanih obaveštenja. Prilagodljiva obaveštenja za Android više nisu podržana."</string>
- <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"Potvrdi"</string>
- <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Isključi"</string>
- <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saznajte više"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Poboljšana obaveštenja su zamenila Android prilagodljiva obaveštenja u Android-u 12. Ova funkcija pokazuje predložene radnje i odgovore, i organizuje obaveštenja.\n\nPoboljšana obaveštenja mogu da pristupaju sadržaju obaveštenja, uključujući lične podatke poput imena kontakata i poruka. Ova funkcija može i da odbacuje obaveštenja ili da odgovara na njih, na primer, da se javlja na telefonske pozive i kontroliše režim Ne uznemiravaj."</string>
- <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Obaveštenje o informacijama Rutinskog režima"</string>
- <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Ušteda baterije je uključena"</string>
- <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Smanjuje se potrošnja baterije da bi se produžilo njeno trajanje"</string>
- <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Ušteda baterije"</string>
- <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Ušteda baterije je isključena"</string>
- <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Baterija telefona je dovoljno napunjena. Funkcije više nisu ograničene."</string>
- <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Baterija tableta je dovoljno napunjena. Funkcije više nisu ograničene."</string>
- <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Baterija uređaja je dovoljno napunjena. Funkcije više nisu ograničene."</string>
- <string name="mime_type_folder" msgid="2203536499348787650">"Folder"</string>
- <string name="mime_type_apk" msgid="3168784749499623902">"Android aplikacija"</string>
- <string name="mime_type_generic" msgid="4606589110116560228">"Datoteka"</string>
- <string name="mime_type_generic_ext" msgid="9220220924380909486">"<xliff:g id="EXTENSION">%1$s</xliff:g> datoteka"</string>
- <string name="mime_type_audio" msgid="4933450584432509875">"Audio datoteka"</string>
- <string name="mime_type_audio_ext" msgid="2615491023840514797">"<xliff:g id="EXTENSION">%1$s</xliff:g> audio datoteka"</string>
- <string name="mime_type_video" msgid="7071965726609428150">"Video"</string>
- <string name="mime_type_video_ext" msgid="185438149044230136">"<xliff:g id="EXTENSION">%1$s</xliff:g> video"</string>
- <string name="mime_type_image" msgid="2134307276151645257">"Slika"</string>
- <string name="mime_type_image_ext" msgid="5743552697560999471">"<xliff:g id="EXTENSION">%1$s</xliff:g> slika"</string>
- <string name="mime_type_compressed" msgid="8737300936080662063">"Arhiva"</string>
- <string name="mime_type_compressed_ext" msgid="4775627287994475737">"<xliff:g id="EXTENSION">%1$s</xliff:g> arhiva"</string>
- <string name="mime_type_document" msgid="3737256839487088554">"Dokument"</string>
- <string name="mime_type_document_ext" msgid="2398002765046677311">"<xliff:g id="EXTENSION">%1$s</xliff:g> dokument"</string>
- <string name="mime_type_spreadsheet" msgid="8188407519131275838">"Tabela"</string>
- <string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> tabela"</string>
- <string name="mime_type_presentation" msgid="1145384236788242075">"Prezentacija"</string>
- <string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> prezentacija"</string>
- <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"Bluetooth ostaje uključen tokom režima rada u avionu"</string>
- <string name="car_loading_profile" msgid="8219978381196748070">"Učitava se"</string>
- <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # fajl}one{{file_name} + # fajl}few{{file_name} + # fajla}other{{file_name} + # fajlova}}"</string>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Nema preporučenih ljudi za deljenje"</string>
- <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista aplikacija"</string>
- <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Ova aplikacija nema dozvolu za snimanje, ali bi mogla da snima zvuk pomoću ovog USB uređaja."</string>
- <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Početak"</string>
- <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Nazad"</string>
- <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Nedavne aplikacije"</string>
- <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Obaveštenja"</string>
- <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Brza podešavanja"</string>
- <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Dijalog napajanja"</string>
- <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Zaključavanje ekrana"</string>
- <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Snimak ekrana"</string>
- <string name="accessibility_system_action_headset_hook_label" msgid="8524691721287425468">"Kuka za slušalice"</string>
- <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Prečica za pristupačnost na ekranu"</string>
- <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Alatka za biranje prečica za pristupačnost na ekranu"</string>
- <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Prečica za pristupačnost"</string>
- <string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Odbaci traku sa obaveštenjima"</string>
- <string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"nagore na D-pad-u"</string>
- <string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"nadole na D-pad-u"</string>
- <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"nalevo na D-pad-u"</string>
- <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"nadesno na D-pad-u"</string>
- <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"centar na D-pad-u"</string>
- <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Traka sa naslovima aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
- <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je dodat u segment OGRANIČENO"</string>
+ <string name="mmcc_authentication_reject" msgid="4891965994643876369">"SIM картица није прилагођена за гласовне услуге"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"SIM картица није подешена за гласовне услуге"</string>
+ <string name="mmcc_illegal_ms" msgid="7509650265233909445">"SIM картица није прилагођена за гласовне услуге"</string>
+ <string name="mmcc_illegal_me" msgid="6505557881889904915">"Телефон није прилагођен за гласовне услуге"</string>
+ <string name="mmcc_authentication_reject_msim_template" msgid="4480853038909922153">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> није дозвољен"</string>
+ <string name="mmcc_imsi_unknown_in_hlr_msim_template" msgid="3688508325248599657">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> није подешен"</string>
+ <string name="mmcc_illegal_ms_msim_template" msgid="832644375774599327">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> није дозвољен"</string>
+ <string name="mmcc_illegal_me_msim_template" msgid="4802735138861422802">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> није дозвољен"</string>
+ <string name="popup_window_default_title" msgid="6907717596694826919">"Искачући прозор"</string>
+ <string name="slice_more_content" msgid="3377367737876888459">"и још <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"Апликација је враћена на старију верзију или није компатибилна са овом пречицом"</string>
+ <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Враћање пречице није успело јер апликација не подржава прављење резервне копије и враћање"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Враћање пречице није успело јер се потписи апликација не подударају"</string>
+ <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Враћање пречице није успело"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Пречица је онемогућена"</string>
+ <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ДЕИНСТАЛИРАЈ"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"ИПАК ОТВОРИ"</string>
+ <string name="harmful_app_warning_title" msgid="8794823880881113856">"Откривена је штетна апликација"</string>
+ <string name="slices_permission_request" msgid="3677129866636153406">"Апликација <xliff:g id="APP_0">%1$s</xliff:g> жели да приказује исечке из апликације <xliff:g id="APP_2">%2$s</xliff:g>"</string>
+ <string name="screenshot_edit" msgid="7408934887203689207">"Измени"</string>
+ <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Вибрација за позиве и обавештења је укључена"</string>
+ <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Мелодија звона за позиве и обавештење је искључена"</string>
+ <string name="notification_channel_system_changes" msgid="2462010596920209678">"Системске промене"</string>
+ <string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"Не узнемиравај"</string>
+ <string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"Ново: Режим Не узнемиравај крије обавештења"</string>
+ <string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"Додирните да бисте сазнали више и променили подешавање."</string>
+ <string name="zen_upgrade_notification_title" msgid="8198167698095298717">"Режим Не узнемиравај је промењен"</string>
+ <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"Додирните да бисте проверили шта је блокирано."</string>
+ <string name="review_notification_settings_title" msgid="5102557424459810820">"Прегледајте подешавања обавештења"</string>
+ <string name="review_notification_settings_text" msgid="5916244866751849279">"Од Android-а 13 апликације које инсталирате морају да имају дозволу за слање обавештења. Додирните да бисте променили ову дозволу за постојеће апликације."</string>
+ <string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"Подсети ме касније"</string>
+ <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Одбаци"</string>
+ <string name="notification_app_name_system" msgid="3045196791746735601">"Систем"</string>
+ <string name="notification_app_name_settings" msgid="9088548800899952531">"Подешавања"</string>
+ <string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
+ <string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string>
+ <string name="notification_appops_overlay_active" msgid="5571732753262836481">"приказује се на екрану док користите друге апликације"</string>
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Пошаљите повратне информације"</string>
+ <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Ово обавештење је унапређено у Подразумевано. Додирните да бисте навели повратне информације."</string>
+ <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Ово обавештење је деградирано у Нечујно. Додирните да бисте навели повратне информације."</string>
+ <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Ово обавештење је рангирано више. Додирните да бисте навели повратне информације."</string>
+ <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Ово обавештење је рангирано ниже. Додирните да бисте навели повратне информације."</string>
+ <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Побољшана обавештења"</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Предложене радње и одговоре сада добијате помоћу побољшаних обавештења. Прилагодљива обавештења за Android више нису подржана."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"Потврди"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Искључи"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Сазнајте више"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Побољшана обавештења су заменила Android прилагодљива обавештења у Android-у 12. Ова функција показује предложене радње и одговоре, и организује обавештења.\n\nПобољшана обавештења могу да приступају садржају обавештења, укључујући личне податке попут имена контаката и порука. Ова функција може и да одбацује обавештења или да одговара на њих, на пример, да се јавља на телефонске позиве и контролише режим Не узнемиравај."</string>
+ <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Обавештење о информацијама Рутинског режима"</string>
+ <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Уштеда батерије је укључена"</string>
+ <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Смањује се потрошња батерије да би се продужило њено трајање"</string>
+ <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Уштеда батерије"</string>
+ <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Уштеда батерије је искључена"</string>
+ <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Батерија телефона је довољно напуњена. Функције више нису ограничене."</string>
+ <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Батерија таблета је довољно напуњена. Функције више нису ограничене."</string>
+ <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Батерија уређаја је довољно напуњена. Функције више нису ограничене."</string>
+ <string name="mime_type_folder" msgid="2203536499348787650">"Фолдер"</string>
+ <string name="mime_type_apk" msgid="3168784749499623902">"Android апликација"</string>
+ <string name="mime_type_generic" msgid="4606589110116560228">"Датотека"</string>
+ <string name="mime_type_generic_ext" msgid="9220220924380909486">"<xliff:g id="EXTENSION">%1$s</xliff:g> датотека"</string>
+ <string name="mime_type_audio" msgid="4933450584432509875">"Аудио датотека"</string>
+ <string name="mime_type_audio_ext" msgid="2615491023840514797">"<xliff:g id="EXTENSION">%1$s</xliff:g> аудио датотека"</string>
+ <string name="mime_type_video" msgid="7071965726609428150">"Видео"</string>
+ <string name="mime_type_video_ext" msgid="185438149044230136">"<xliff:g id="EXTENSION">%1$s</xliff:g> видео"</string>
+ <string name="mime_type_image" msgid="2134307276151645257">"Слика"</string>
+ <string name="mime_type_image_ext" msgid="5743552697560999471">"<xliff:g id="EXTENSION">%1$s</xliff:g> слика"</string>
+ <string name="mime_type_compressed" msgid="8737300936080662063">"Архива"</string>
+ <string name="mime_type_compressed_ext" msgid="4775627287994475737">"<xliff:g id="EXTENSION">%1$s</xliff:g> архива"</string>
+ <string name="mime_type_document" msgid="3737256839487088554">"Документ"</string>
+ <string name="mime_type_document_ext" msgid="2398002765046677311">"<xliff:g id="EXTENSION">%1$s</xliff:g> документ"</string>
+ <string name="mime_type_spreadsheet" msgid="8188407519131275838">"Табела"</string>
+ <string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> табела"</string>
+ <string name="mime_type_presentation" msgid="1145384236788242075">"Презентација"</string>
+ <string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> презентација"</string>
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"Bluetooth остаје укључен током режима рада у авиону"</string>
+ <string name="car_loading_profile" msgid="8219978381196748070">"Учитава се"</string>
+ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # фајл}one{{file_name} + # фајл}few{{file_name} + # фајла}other{{file_name} + # фајлова}}"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Нема препоручених људи за дељење"</string>
+ <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Листа апликација"</string>
+ <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Ова апликација нема дозволу за снимање, али би могла да снима звук помоћу овог USB уређаја."</string>
+ <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Почетак"</string>
+ <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Назад"</string>
+ <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Недавне апликације"</string>
+ <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Обавештења"</string>
+ <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Брза подешавања"</string>
+ <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Дијалог напајања"</string>
+ <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Закључавање екрана"</string>
+ <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Снимак екрана"</string>
+ <string name="accessibility_system_action_headset_hook_label" msgid="8524691721287425468">"Кука за слушалице"</string>
+ <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Пречица за приступачност на екрану"</string>
+ <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Алатка за бирање пречица за приступачност на екрану"</string>
+ <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Пречица за приступачност"</string>
+ <string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Одбаци траку са обавештењима"</string>
+ <string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"нагоре на D-pad-у"</string>
+ <string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"надоле на D-pad-у"</string>
+ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"налево на D-pad-у"</string>
+ <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"надесно на D-pad-у"</string>
+ <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"центар на D-pad-у"</string>
+ <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Трака са насловима апликације <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> је додат у сегмент ОГРАНИЧЕНО"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
- <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslao/la sliku"</string>
- <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Konverzacija"</string>
- <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Grupna konverzacija"</string>
+ <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"је послао/ла слику"</string>
+ <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Конверзација"</string>
+ <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Групна конверзација"</string>
<string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
- <string name="resolver_personal_tab" msgid="2051260504014442073">"Lično"</string>
- <string name="resolver_work_tab" msgid="2690019516263167035">"Poslovno"</string>
- <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Lični prikaz"</string>
- <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Prikaz za posao"</string>
- <string name="resolver_cross_profile_blocked" msgid="3014597376026044840">"Blokira IT administrator"</string>
- <string name="resolver_cant_share_with_work_apps_explanation" msgid="9071442683080586643">"Ovaj sadržaj ne može da se deli pomoću poslovnih aplikacija"</string>
- <string name="resolver_cant_access_work_apps_explanation" msgid="1129960195389373279">"Ovaj sadržaj ne može da se otvara pomoću poslovnih aplikacija"</string>
- <string name="resolver_cant_share_with_personal_apps_explanation" msgid="6349766201904601544">"Ovaj sadržaj ne može da se deli pomoću ličnih aplikacija"</string>
- <string name="resolver_cant_access_personal_apps_explanation" msgid="1679399548862724359">"Ovaj sadržaj ne može da se otvara pomoću ličnih aplikacija"</string>
- <string name="resolver_turn_on_work_apps" msgid="884910835250037247">"Poslovni profil je pauziran"</string>
- <string name="resolver_switch_on_work" msgid="463709043650610420">"Dodirnite da biste uključili"</string>
- <string name="resolver_no_work_apps_available" msgid="3298291360133337270">"Nema poslovnih aplikacija"</string>
- <string name="resolver_no_personal_apps_available" msgid="6284837227019594881">"Nema ličnih aplikacija"</string>
- <string name="miniresolver_open_in_personal" msgid="3874522693661065566">"Želite da na ličnom profilu otvorite: <xliff:g id="APP">%s</xliff:g>?"</string>
- <string name="miniresolver_open_in_work" msgid="4415223793669536559">"Želite da na poslovnom profilu otvorite: <xliff:g id="APP">%s</xliff:g>?"</string>
- <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Koristi lični pregledač"</string>
- <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Koristi poslovni pregledač"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"PIN za otključavanje SIM mreže"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"PIN za otključavanje podskupa SIM mreže"</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY" msgid="4447629474818217364">"PIN za otključavanje poslovne SIM kartice"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY" msgid="973059024670737358">"PIN za otključavanje dobavljača usluge SIM kartice"</string>
- <string name="PERSOSUBSTATE_SIM_SIM_ENTRY" msgid="4487435301206073787">"PIN za otključavanje SIM kartice"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY" msgid="768060297218652809">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY" msgid="7129527319490548930">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ENTRY" msgid="2876126640607573252">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ENTRY" msgid="8952595089930109282">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_SIM_SIM_PUK_ENTRY" msgid="3013902515773728996">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK1_ENTRY" msgid="2974411408893410289">"PIN za otključavanje RUIM mreže 1"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK2_ENTRY" msgid="687618528751880721">"PIN za otključavanje RUIM mreže 2"</string>
- <string name="PERSOSUBSTATE_RUIM_HRPD_ENTRY" msgid="6810596579655575381">"PIN za otključavanje RUIM hrpd-a"</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY" msgid="2715929642540980259">"PIN za otključavanje poslovne RUIM kartice"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY" msgid="8557791623303951590">"PIN za otključavanje RUIM kartice dobavljača usluge"</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY" msgid="7382468767274580323">"PIN za otključavanje RUIM kartice"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY" msgid="6730880791104286987">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY" msgid="6432126539782267026">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ENTRY" msgid="1730510161529488920">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ENTRY" msgid="3369885925003346830">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ENTRY" msgid="9129139686191167829">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ENTRY" msgid="2869929685874615358">"Unesite PUK"</string>
- <string name="PERSOSUBSTATE_SIM_SPN_ENTRY" msgid="1238663472392741771">"PIN za otključavanje SPN-a"</string>
- <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY" msgid="3988705848553894358">"PIN za otključavanje SP ekvivalentnog matičnog PLMN-a"</string>
- <string name="PERSOSUBSTATE_SIM_ICCID_ENTRY" msgid="6186770686690993200">"PIN za otključavanje ICCID-a"</string>
- <string name="PERSOSUBSTATE_SIM_IMPI_ENTRY" msgid="7043865376145617024">"PIN za otključavanje IMPI-ja"</string>
- <string name="PERSOSUBSTATE_SIM_NS_SP_ENTRY" msgid="6144227308185112176">"PIN za otključavanje dobavljača usluge podskupa mreže"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_IN_PROGRESS" msgid="4233355366318061180">"Zahteva se otključavanje SIM mreže…"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_IN_PROGRESS" msgid="6742563947637715645">"Zahteva se otključavanje podskupa SIM mreže…"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_IN_PROGRESS" msgid="2033399698172403560">"Zahteva se otključavanje dobavljača usluge SIM kartice…"</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_IN_PROGRESS" msgid="4795977251920732254">"Zahteva se otključavanje poslovne SIM kartice…"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_IN_PROGRESS" msgid="1090425878157254446">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_IN_PROGRESS" msgid="6476898876518094438">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_IN_PROGRESS" msgid="6006806734293747731">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_IN_PROGRESS" msgid="6546680489620881893">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_SIM_SIM_PUK_IN_PROGRESS" msgid="3506845511000727015">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_SIM_SIM_IN_PROGRESS" msgid="6709169861932992750">"Zahteva se otključavanje SIM kartice…"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK1_IN_PROGRESS" msgid="4013870911606478520">"Zahteva se otključavanje RUIM mreže 1…"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK2_IN_PROGRESS" msgid="9032651188219523434">"Zahteva se otključavanje RUIM mreže 2…"</string>
- <string name="PERSOSUBSTATE_RUIM_HRPD_IN_PROGRESS" msgid="6584576506344491207">"Zahteva se otključavanje RUIM hrpd-a…"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_IN_PROGRESS" msgid="830981927724888114">"Zahteva se otključavanje RUIM kartice dobavljača usluge…"</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_IN_PROGRESS" msgid="7851790973098894802">"Zahteva se otključavanje poslovne RUIM kartice…"</string>
- <string name="PERSOSUBSTATE_SIM_SPN_IN_PROGRESS" msgid="1149560739586960121">"Zahteva se otključavanje SPN-a…"</string>
- <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_IN_PROGRESS" msgid="5708964693522116025">"Zahteva se otključavanje SP ekvivalentnog matičnog PLMN-a…"</string>
- <string name="PERSOSUBSTATE_SIM_ICCID_IN_PROGRESS" msgid="7288103122966483455">"Zahteva se otključavanje ICCID-a…"</string>
- <string name="PERSOSUBSTATE_SIM_IMPI_IN_PROGRESS" msgid="4036752174056147753">"Zahteva se otključavanje IMPI-ja…"</string>
- <string name="PERSOSUBSTATE_SIM_NS_SP_IN_PROGRESS" msgid="5089536274515338566">"Zahteva se otključavanje dobavljača usluge podskupa mreže…"</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_IN_PROGRESS" msgid="6737197986936251958">"Zahteva se otključavanje RUIM kartice…"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_IN_PROGRESS" msgid="5658767775619998623">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_IN_PROGRESS" msgid="665978313257653727">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_IN_PROGRESS" msgid="3857142652251836850">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_IN_PROGRESS" msgid="2695664012344346788">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_IN_PROGRESS" msgid="2695678959963807782">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_IN_PROGRESS" msgid="1230605365926493599">"Zahteva se otključavanje pomoću PUK-a…"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_ERROR" msgid="1924844017037151535">"Zahtev za otključavanje SIM mreže nije uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR" msgid="3372797822292089708">"Zahtev za otključavanje podskupa SIM mreže je uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR" msgid="1878443146720411381">"Zahtev za otključavanje dobavljača usluge SIM kartice nije uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_ERROR" msgid="7664778312218023192">"Zahtev za otključavanje poslovne SIM kartice nije uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_SIM_ERROR" msgid="2472944311643350302">"Zahtev za otključavanje SIM kartice nije uspeo."</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK1_ERROR" msgid="828089694480999120">"Zahtev za otključavanje RUIM mreže 1 nije uspeo."</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK2_ERROR" msgid="17619001007092511">"Zahtev za otključavanje RUIM mreže 2 nije uspeo."</string>
- <string name="PERSOSUBSTATE_RUIM_HRPD_ERROR" msgid="807214229604353614">"Zahtev za otključavanje RUIM hrpd-a nije uspeo."</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_ERROR" msgid="8644184447744175747">"Zahtev za otključavanje poslovne RUIM kartice nije uspeo."</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR" msgid="3801002648649640407">"Zahtev za otključavanje RUIM kartice dobavljača usluge nije uspeo."</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_ERROR" msgid="707397021218680753">"Zahtev za otključavanje RUIM kartice nije uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ERROR" msgid="894358680773257820">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ERROR" msgid="352466878146726991">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ERROR" msgid="7353389721907138671">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ERROR" msgid="2655263155490857920">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_SIM_PUK_ERROR" msgid="6903740900892931310">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ERROR" msgid="5165901670447518687">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ERROR" msgid="2856763216589267623">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ERROR" msgid="817542684437829139">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ERROR" msgid="5178635064113393143">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ERROR" msgid="5391587926974531008">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ERROR" msgid="4895494864493315868">"Otključavanje pomoću PUK-a nije uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_SPN_ERROR" msgid="9017576601595353649">"Zahtev za otključavanje SPN-a nije uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR" msgid="1116993930995545742">"Zahtev za otključavanje SP ekvivalentnog matičnog PLMN-a nije uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_ICCID_ERROR" msgid="7559167306794441462">"Zahtev za otključavanje ICCID-a nije uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_IMPI_ERROR" msgid="2782926139511136588">"Zahtev za otključavanje IMPI-ja nije uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR" msgid="1890493954453456758">"Zahtev za otključavanje dobavljača usluge podskupa mreže nije uspeo."</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUCCESS" msgid="4886243367747126325">"Otključavanje SIM mreže je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_SUCCESS" msgid="4053809277733513987">"Otključavanje podskupa SIM mreže je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_SUCCESS" msgid="8249342930499801740">"Otključavanje dobavljača usluge SIM kartice je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_SUCCESS" msgid="2339794542560381270">"Otključavanje poslovne SIM kartice je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_SIM_SUCCESS" msgid="6975608174152828954">"Otključavanje SIM kartice je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK1_SUCCESS" msgid="2846699261330463192">"Otključavanje RUIM mreže 1 je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK2_SUCCESS" msgid="5335414726057102801">"Otključavanje RUIM mreže 2 je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_HRPD_SUCCESS" msgid="8868100318474971969">"Otključavanje RUIM hrpd-a je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_SUCCESS" msgid="6020936629725666932">"Zahtev za otključavanje RUIM kartice dobavljača usluge je uspeo."</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS" msgid="6944873647584595489">"Otključavanje poslovne RUIM kartice je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_SUCCESS" msgid="2526483514124121988">"Otključavanje RUIM kartice je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_SUCCESS" msgid="7662200333621664621">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_SUCCESS" msgid="2861223407953766632">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_SUCCESS" msgid="5345648571175243272">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_SUCCESS" msgid="3725278343103422466">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_SIM_PUK_SUCCESS" msgid="6998502547560297983">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_SUCCESS" msgid="8555433771162560361">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_SUCCESS" msgid="3555767296933606232">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_SUCCESS" msgid="6778051818199974237">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_SUCCESS" msgid="4080108758498911429">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_SUCCESS" msgid="7873675303000794343">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_SUCCESS" msgid="1763198215069819523">"Otključavanje pomoću PUK-a je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_SPN_SUCCESS" msgid="2053891977727320532">"Otključavanje SPN-a je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_SUCCESS" msgid="8146602361895007345">"Otključavanje SP ekvivalentnog matičnog PLMN-a je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_ICCID_SUCCESS" msgid="8058678548991999545">"Otključavanje ICCID-a je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_IMPI_SUCCESS" msgid="2545608067978550571">"Otključavanje IMPI-ja je uspelo."</string>
- <string name="PERSOSUBSTATE_SIM_NS_SP_SUCCESS" msgid="4352382949744625007">"Zahtev za otključavanje dobavljača usluge podskupa mreže je uspeo."</string>
+ <string name="resolver_personal_tab" msgid="2051260504014442073">"Лично"</string>
+ <string name="resolver_work_tab" msgid="2690019516263167035">"Пословно"</string>
+ <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Лични приказ"</string>
+ <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Приказ за посао"</string>
+ <string name="resolver_cross_profile_blocked" msgid="3014597376026044840">"Блокира ИТ администратор"</string>
+ <string name="resolver_cant_share_with_work_apps_explanation" msgid="9071442683080586643">"Овај садржај не може да се дели помоћу пословних апликација"</string>
+ <string name="resolver_cant_access_work_apps_explanation" msgid="1129960195389373279">"Овај садржај не може да се отвара помоћу пословних апликација"</string>
+ <string name="resolver_cant_share_with_personal_apps_explanation" msgid="6349766201904601544">"Овај садржај не може да се дели помоћу личних апликација"</string>
+ <string name="resolver_cant_access_personal_apps_explanation" msgid="1679399548862724359">"Овај садржај не може да се отвара помоћу личних апликација"</string>
+ <string name="resolver_turn_on_work_apps" msgid="884910835250037247">"Пословни профил је паузиран"</string>
+ <string name="resolver_switch_on_work" msgid="463709043650610420">"Додирните да бисте укључили"</string>
+ <string name="resolver_no_work_apps_available" msgid="3298291360133337270">"Нема пословних апликација"</string>
+ <string name="resolver_no_personal_apps_available" msgid="6284837227019594881">"Нема личних апликација"</string>
+ <string name="miniresolver_open_in_personal" msgid="3874522693661065566">"Желите да на личном профилу отворите: <xliff:g id="APP">%s</xliff:g>?"</string>
+ <string name="miniresolver_open_in_work" msgid="4415223793669536559">"Желите да на пословном профилу отворите: <xliff:g id="APP">%s</xliff:g>?"</string>
+ <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Користи лични прегледач"</string>
+ <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Користи пословни прегледач"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"PIN за откључавање SIM мреже"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"PIN за откључавање подскупа SIM мреже"</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY" msgid="4447629474818217364">"PIN за откључавање пословне SIM картице"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY" msgid="973059024670737358">"PIN за откључавање добављача услуге SIM картице"</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_ENTRY" msgid="4487435301206073787">"PIN за откључавање SIM картице"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY" msgid="768060297218652809">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY" msgid="7129527319490548930">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ENTRY" msgid="2876126640607573252">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ENTRY" msgid="8952595089930109282">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_PUK_ENTRY" msgid="3013902515773728996">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK1_ENTRY" msgid="2974411408893410289">"PIN за откључавање RUIM мреже 1"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK2_ENTRY" msgid="687618528751880721">"PIN за откључавање RUIM мреже 2"</string>
+ <string name="PERSOSUBSTATE_RUIM_HRPD_ENTRY" msgid="6810596579655575381">"PIN за откључавање RUIM hrpd-а"</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY" msgid="2715929642540980259">"PIN за откључавање пословне RUIM картице"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY" msgid="8557791623303951590">"PIN за откључавање RUIM картице добављача услуге"</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY" msgid="7382468767274580323">"PIN за откључавање RUIM картице"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY" msgid="6730880791104286987">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY" msgid="6432126539782267026">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ENTRY" msgid="1730510161529488920">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ENTRY" msgid="3369885925003346830">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ENTRY" msgid="9129139686191167829">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ENTRY" msgid="2869929685874615358">"Унесите PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_SPN_ENTRY" msgid="1238663472392741771">"PIN за откључавање SPN-а"</string>
+ <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY" msgid="3988705848553894358">"PIN за откључавање SP еквивалентног матичног PLMN-а"</string>
+ <string name="PERSOSUBSTATE_SIM_ICCID_ENTRY" msgid="6186770686690993200">"PIN за откључавање ICCID-а"</string>
+ <string name="PERSOSUBSTATE_SIM_IMPI_ENTRY" msgid="7043865376145617024">"PIN за откључавање IMPI-ја"</string>
+ <string name="PERSOSUBSTATE_SIM_NS_SP_ENTRY" msgid="6144227308185112176">"PIN за откључавање добављача услуге подскупа мреже"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_IN_PROGRESS" msgid="4233355366318061180">"Захтева се откључавање SIM мреже…"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_IN_PROGRESS" msgid="6742563947637715645">"Захтева се откључавање подскупа SIM мреже…"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_IN_PROGRESS" msgid="2033399698172403560">"Захтева се откључавање добављача услуге SIM картице…"</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_IN_PROGRESS" msgid="4795977251920732254">"Захтева се откључавање пословне SIM картице…"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_IN_PROGRESS" msgid="1090425878157254446">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_IN_PROGRESS" msgid="6476898876518094438">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_IN_PROGRESS" msgid="6006806734293747731">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_IN_PROGRESS" msgid="6546680489620881893">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_PUK_IN_PROGRESS" msgid="3506845511000727015">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_IN_PROGRESS" msgid="6709169861932992750">"Захтева се откључавање SIM картице…"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK1_IN_PROGRESS" msgid="4013870911606478520">"Захтева се откључавање RUIM мреже 1…"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK2_IN_PROGRESS" msgid="9032651188219523434">"Захтева се откључавање RUIM мреже 2…"</string>
+ <string name="PERSOSUBSTATE_RUIM_HRPD_IN_PROGRESS" msgid="6584576506344491207">"Захтева се откључавање RUIM hrpd-а…"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_IN_PROGRESS" msgid="830981927724888114">"Захтева се откључавање RUIM картице добављача услуге…"</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_IN_PROGRESS" msgid="7851790973098894802">"Захтева се откључавање пословне RUIM картице…"</string>
+ <string name="PERSOSUBSTATE_SIM_SPN_IN_PROGRESS" msgid="1149560739586960121">"Захтева се откључавање SPN-а…"</string>
+ <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_IN_PROGRESS" msgid="5708964693522116025">"Захтева се откључавање SP еквивалентног матичног PLMN-а…"</string>
+ <string name="PERSOSUBSTATE_SIM_ICCID_IN_PROGRESS" msgid="7288103122966483455">"Захтева се откључавање ICCID-а…"</string>
+ <string name="PERSOSUBSTATE_SIM_IMPI_IN_PROGRESS" msgid="4036752174056147753">"Захтева се откључавање IMPI-ја…"</string>
+ <string name="PERSOSUBSTATE_SIM_NS_SP_IN_PROGRESS" msgid="5089536274515338566">"Захтева се откључавање добављача услуге подскупа мреже…"</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_IN_PROGRESS" msgid="6737197986936251958">"Захтева се откључавање RUIM картице…"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_IN_PROGRESS" msgid="5658767775619998623">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_IN_PROGRESS" msgid="665978313257653727">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_IN_PROGRESS" msgid="3857142652251836850">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_IN_PROGRESS" msgid="2695664012344346788">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_IN_PROGRESS" msgid="2695678959963807782">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_IN_PROGRESS" msgid="1230605365926493599">"Захтева се откључавање помоћу PUK-а…"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_ERROR" msgid="1924844017037151535">"Захтев за откључавање SIM мреже није успео."</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR" msgid="3372797822292089708">"Захтев за откључавање подскупа SIM мреже је успео."</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR" msgid="1878443146720411381">"Захтев за откључавање добављача услуге SIM картице није успео."</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_ERROR" msgid="7664778312218023192">"Захтев за откључавање пословне SIM картице није успео."</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_ERROR" msgid="2472944311643350302">"Захтев за откључавање SIM картице није успео."</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK1_ERROR" msgid="828089694480999120">"Захтев за откључавање RUIM мреже 1 није успео."</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK2_ERROR" msgid="17619001007092511">"Захтев за откључавање RUIM мреже 2 није успео."</string>
+ <string name="PERSOSUBSTATE_RUIM_HRPD_ERROR" msgid="807214229604353614">"Захтев за откључавање RUIM hrpd-а није успео."</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_ERROR" msgid="8644184447744175747">"Захтев за откључавање пословне RUIM картице није успео."</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR" msgid="3801002648649640407">"Захтев за откључавање RUIM картице добављача услуге није успео."</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_ERROR" msgid="707397021218680753">"Захтев за откључавање RUIM картице није успео."</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ERROR" msgid="894358680773257820">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ERROR" msgid="352466878146726991">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ERROR" msgid="7353389721907138671">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ERROR" msgid="2655263155490857920">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_PUK_ERROR" msgid="6903740900892931310">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ERROR" msgid="5165901670447518687">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ERROR" msgid="2856763216589267623">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ERROR" msgid="817542684437829139">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ERROR" msgid="5178635064113393143">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ERROR" msgid="5391587926974531008">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ERROR" msgid="4895494864493315868">"Откључавање помоћу PUK-а није успело."</string>
+ <string name="PERSOSUBSTATE_SIM_SPN_ERROR" msgid="9017576601595353649">"Захтев за откључавање SPN-а није успео."</string>
+ <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR" msgid="1116993930995545742">"Захтев за откључавање SP еквивалентног матичног PLMN-а није успео."</string>
+ <string name="PERSOSUBSTATE_SIM_ICCID_ERROR" msgid="7559167306794441462">"Захтев за откључавање ICCID-а није успео."</string>
+ <string name="PERSOSUBSTATE_SIM_IMPI_ERROR" msgid="2782926139511136588">"Захтев за откључавање IMPI-ја није успео."</string>
+ <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR" msgid="1890493954453456758">"Захтев за откључавање добављача услуге подскупа мреже није успео."</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUCCESS" msgid="4886243367747126325">"Откључавање SIM мреже је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_SUCCESS" msgid="4053809277733513987">"Откључавање подскупа SIM мреже је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_SUCCESS" msgid="8249342930499801740">"Откључавање добављача услуге SIM картице је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_SUCCESS" msgid="2339794542560381270">"Откључавање пословне SIM картице је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_SUCCESS" msgid="6975608174152828954">"Откључавање SIM картице је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK1_SUCCESS" msgid="2846699261330463192">"Откључавање RUIM мреже 1 је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK2_SUCCESS" msgid="5335414726057102801">"Откључавање RUIM мреже 2 је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_HRPD_SUCCESS" msgid="8868100318474971969">"Откључавање RUIM hrpd-а је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_SUCCESS" msgid="6020936629725666932">"Захтев за откључавање RUIM картице добављача услуге је успео."</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS" msgid="6944873647584595489">"Откључавање пословне RUIM картице је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_SUCCESS" msgid="2526483514124121988">"Откључавање RUIM картице је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_SUCCESS" msgid="7662200333621664621">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_SUCCESS" msgid="2861223407953766632">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_SUCCESS" msgid="5345648571175243272">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_SUCCESS" msgid="3725278343103422466">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_PUK_SUCCESS" msgid="6998502547560297983">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_SUCCESS" msgid="8555433771162560361">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_SUCCESS" msgid="3555767296933606232">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_SUCCESS" msgid="6778051818199974237">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_SUCCESS" msgid="4080108758498911429">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_SUCCESS" msgid="7873675303000794343">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_SUCCESS" msgid="1763198215069819523">"Откључавање помоћу PUK-а је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_SPN_SUCCESS" msgid="2053891977727320532">"Откључавање SPN-а је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_SUCCESS" msgid="8146602361895007345">"Откључавање SP еквивалентног матичног PLMN-а је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_ICCID_SUCCESS" msgid="8058678548991999545">"Откључавање ICCID-а је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_IMPI_SUCCESS" msgid="2545608067978550571">"Откључавање IMPI-ја је успело."</string>
+ <string name="PERSOSUBSTATE_SIM_NS_SP_SUCCESS" msgid="4352382949744625007">"Захтев за откључавање добављача услуге подскупа мреже је успео."</string>
<string name="config_pdp_reject_dialog_title" msgid="4072057179246785727"></string>
<string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
- <string name="window_magnification_prompt_title" msgid="2876703640772778215">"Nova podešavanja uvećanja"</string>
- <string name="window_magnification_prompt_content" msgid="8159173903032344891">"Sada možete da uvećate deo ekrana"</string>
- <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Uključite u Podešavanjima"</string>
- <string name="dismiss_action" msgid="1728820550388704784">"Odbaci"</string>
- <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Odblokirajte mikrofon uređaja"</string>
- <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Odblokirajte kameru uređaja"</string>
- <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Za &lt;b&gt;<xliff:g id="APP">%s</xliff:g>&lt;/b&gt; i sve aplikacije i usluge"</string>
- <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Odblokiraj"</string>
- <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatnost senzora"</string>
- <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona aplikacije"</string>
- <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imidž brenda aplikacije"</string>
- <string name="view_and_control_notification_title" msgid="4300765399209912240">"Proverite podešavanja pristupa"</string>
- <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> može da pregleda i kontroliše ekran. Dodirnite da biste pregledali."</string>
- <string name="ui_translation_accessibility_translated_text" msgid="3197547218178944544">"<xliff:g id="MESSAGE">%1$s</xliff:g> Prevedeno."</string>
- <string name="ui_translation_accessibility_translation_finished" msgid="3057830947610088465">"Poruka je prevedena sa jezika <xliff:g id="FROM_LANGUAGE">%1$s</xliff:g> na <xliff:g id="TO_LANGUAGE">%2$s</xliff:g>."</string>
- <string name="notification_channel_abusive_bg_apps" msgid="6092140213264920355">"Aktivnost u pozadini"</string>
- <string name="notification_title_abusive_bg_apps" msgid="994230770856147656">"Aplikacija vam prazni bateriju"</string>
- <string name="notification_title_long_running_fgs" msgid="8170284286477131587">"Aplikacija je i dalje aktivna"</string>
- <string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> radi u pozadini. Dodirnite da biste upravljali potrošnjom baterije."</string>
- <string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> može da utiče na trajanje baterije. Dodirnite da biste pregledali aktivne aplikacije."</string>
- <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Proverite aktivne aplikacije"</string>
- <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ne može da se pristupi kameri telefona sa <xliff:g id="DEVICE">%1$s</xliff:g> uređaja"</string>
- <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ne može da se pristupi kameri tableta sa <xliff:g id="DEVICE">%1$s</xliff:g> uređaja"</string>
- <string name="vdm_secure_window" msgid="161700398158812314">"Ovom ne možete da pristupate tokom strimovanja. Probajte na telefonu."</string>
- <string name="vdm_pip_blocked" msgid="4036107522497281397">"Ne možete da gledate sliku u slici pri strimovanju"</string>
- <string name="system_locale_title" msgid="711882686834677268">"Podrazumevani sistemski"</string>
- <string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
- <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Dozvola za profil pratećeg sata za upravljanje satovima"</string>
- <string name="permdesc_companionProfileWatch" msgid="5655698581110449397">"Dozvoljava pratećoj aplikaciji da upravlja satovima."</string>
- <string name="permlab_observeCompanionDevicePresence" msgid="9008994909653990465">"Nadgledanje prisustva pratećeg uređaja"</string>
- <string name="permdesc_observeCompanionDevicePresence" msgid="3011699826788697852">"Dozvoljava pratećoj aplikaciji da nadgleda prisustvo pratećeg uređaja kada su uređaji u blizini ili daleko."</string>
- <string name="permlab_deliverCompanionMessages" msgid="3931552294842980887">"Isporuka poruka iz prateće aplikacije"</string>
- <string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Dozvoljava pratećoj aplikaciji da šalje prateće poruke na druge uređaje."</string>
- <string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Pokretanje usluga u prvom planu iz pozadine"</string>
- <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Dozvoljava pratećoj aplikaciji da pokrene usluge u prvom planu iz pozadine."</string>
+ <string name="window_magnification_prompt_title" msgid="2876703640772778215">"Нова подешавања увећања"</string>
+ <string name="window_magnification_prompt_content" msgid="8159173903032344891">"Сада можете да увећате део екрана"</string>
+ <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Укључите у Подешавањима"</string>
+ <string name="dismiss_action" msgid="1728820550388704784">"Одбаци"</string>
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Одблокирајте микрофон уређаја"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Одблокирајте камеру уређаја"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"За &lt;b&gt;<xliff:g id="APP">%s</xliff:g>&lt;/b&gt; и све апликације и услуге"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Одблокирај"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Приватност сензора"</string>
+ <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Икона апликације"</string>
+ <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Имиџ бренда апликације"</string>
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Проверите подешавања приступа"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> може да прегледа и контролише екран. Додирните да бисте прегледали."</string>
+ <string name="ui_translation_accessibility_translated_text" msgid="3197547218178944544">"<xliff:g id="MESSAGE">%1$s</xliff:g> Преведено."</string>
+ <string name="ui_translation_accessibility_translation_finished" msgid="3057830947610088465">"Порука је преведена са језика <xliff:g id="FROM_LANGUAGE">%1$s</xliff:g> на <xliff:g id="TO_LANGUAGE">%2$s</xliff:g>."</string>
+ <string name="notification_channel_abusive_bg_apps" msgid="6092140213264920355">"Активност у позадини"</string>
+ <string name="notification_title_abusive_bg_apps" msgid="994230770856147656">"Апликација вам празни батерију"</string>
+ <string name="notification_title_long_running_fgs" msgid="8170284286477131587">"Апликација је и даље активна"</string>
+ <string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"Апликација <xliff:g id="APP">%1$s</xliff:g> ради у позадини. Додирните да бисте управљали потрошњом батерије."</string>
+ <string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> може да утиче на трајање батерије. Додирните да бисте прегледали активне апликације."</string>
+ <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Проверите активне апликације"</string>
+ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не може да се приступи камери телефона са <xliff:g id="DEVICE">%1$s</xliff:g> уређаја"</string>
+ <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не може да се приступи камери таблета са <xliff:g id="DEVICE">%1$s</xliff:g> уређаја"</string>
+ <string name="vdm_secure_window" msgid="161700398158812314">"Овом не можете да приступате током стримовања. Пробајте на телефону."</string>
+ <string name="vdm_pip_blocked" msgid="4036107522497281397">"Не можете да гледате слику у слици при стримовању"</string>
+ <string name="system_locale_title" msgid="711882686834677268">"Подразумевани системски"</string>
+ <string name="default_card_name" msgid="9198284935962911468">"КАРТИЦА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
+ <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дозвола за профил пратећег сата за управљање сатовима"</string>
+ <string name="permdesc_companionProfileWatch" msgid="5655698581110449397">"Дозвољава пратећој апликацији да управља сатовима."</string>
+ <string name="permlab_observeCompanionDevicePresence" msgid="9008994909653990465">"Надгледање присуства пратећег уређаја"</string>
+ <string name="permdesc_observeCompanionDevicePresence" msgid="3011699826788697852">"Дозвољава пратећој апликацији да надгледа присуство пратећег уређаја када су уређаји у близини или далеко."</string>
+ <string name="permlab_deliverCompanionMessages" msgid="3931552294842980887">"Испорука порука из пратеће апликације"</string>
+ <string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Дозвољава пратећој апликацији да шаље пратеће поруке на друге уређаје."</string>
+ <string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Покретање услуга у првом плану из позадине"</string>
+ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Дозвољава пратећој апликацији да покрене услуге у првом плану из позадине."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 45e14c055866..e13627550607 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -2354,4 +2354,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Спадарожная праграма зможа дастаўляць паведамленні на іншыя прылады."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Запуск актыўных сэрвісаў з фонавага рэжыму"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Спадарожная праграма зможа запускаць актыўныя сэрвісы з фонавага рэжыму."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 8acc09bdfd1d..fd6bd3e0c381 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Разрешава на дадено придружаващо приложение да доставя придружаващи съобщения до други устройства."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Стартиране на услуги на преден план при изпълнение на заден план"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Разрешава на дадено придружаващо приложение да стартира услуги на преден план, докато се изпълнява на заден план."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 2aa76b9dad36..4da5520e8a66 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"অন্যান্য ডিভাইসে কম্প্যানিয়ন মেসেজ ডেলিভার করতে কম্প্যানিয়ন অ্যাপকে অনুমতি দেয়।"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"ব্যাকগ্রাউন্ড থেকে ফোরগ্রাউন্ড পরিষেবা চালু করা"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"কম্প্যানিয়ন অ্যাপকে, ব্যাকগ্রাউন্ড থেকে ফোরগ্রাউন্ড পরিষেবা চালু করার অনুমতি দেয়।"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 4f50c18498ac..67574efcbbb8 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Dozvoljava pratećoj aplikaciji da isporučuje prateće poruke na drugim uređajima."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Pokreni usluge u prvom planu iz pozadine"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Dozvoljava pratećoj aplikaciji da iz pozadine pokrene usluge u prvom planu."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 55f8c5bc5b16..080501dd758b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Permet que una aplicació complementària enviï missatges complementaris a altres dispositius."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Inicia serveis en primer pla des d\'un segon pla"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permet que una aplicació complementària iniciï serveis en primer pla des d\'un segon pla."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 46d8cfce2938..2a6383f38b5b 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -2354,4 +2354,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Umožňuje doprovodné aplikaci doručovat doprovodné zprávy do jiných zařízení."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Spouštět z pozadí služby v popředí"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Umožňuje doprovodné aplikaci spouštět z pozadí služby v popředí."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 6e1a0c24e8a8..74589fe5a54c 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Tillader, at en medfølgende app kan levere medfølgende meddelelser til andre enheder."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Start tjenester i forgrunden via tilladelser til tjenester i baggrunden"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Tillader, at en medfølgende app kan starte tjenester i forgrunden via tilladelser til tjenester i baggrunden."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ccc2a19eb59f..9cb496e64801 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Ermöglicht einer Companion-App, Companion-Nachrichten an andere Geräte zu senden."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Dienste im Vordergrund aus dem Hintergrund starten"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Ermöglicht einer Companion-App, Dienste im Vordergrund aus dem Hintergrund zu starten."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 85d34551cec1..dc3bc45d43cf 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Επιτρέπει σε μια συνοδευτική εφαρμογή να παρέχει μηνύματα σε άλλες συσκευές."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Εκκίνηση υπηρεσιών στο προσκήνιο από το παρασκήνιο"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Επιτρέπει σε μια συνοδευτική εφαρμογή να εκκινεί υπηρεσίες στο προσκήνιο από το παρασκήνιο."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index abe900da2144..1b1a0c42c577 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -43,10 +43,8 @@
<string name="mismatchPin" msgid="2929611853228707473">"The PINs that you typed don\'t match."</string>
<string name="invalidPin" msgid="7542498253319440408">"Type a PIN that is 4 to 8 numbers."</string>
<string name="invalidPuk" msgid="8831151490931907083">"Type a PUK that is 8 numbers or longer."</string>
- <!-- no translation found for needPuk (3503414069503752211) -->
- <skip />
- <!-- no translation found for needPuk2 (3910763547447344963) -->
- <skip />
+ <string name="needPuk" msgid="3503414069503752211">"Your SIM is PUK-locked. Type the PUK code to unlock it."</string>
+ <string name="needPuk2" msgid="3910763547447344963">"Type PUK2 to unblock SIM."</string>
<string name="enablePin" msgid="2543771964137091212">"Unsuccessful, enable SIM/RUIM Lock."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="other">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
@@ -963,22 +961,14 @@
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Unlock for all features and data"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum Face Unlock attempts exceeded"</string>
- <!-- no translation found for lockscreen_missing_sim_message_short (1229301273156907613) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3986843848305639161) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3903140876952198273) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (6184187634180854181) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions (5823469004536805423) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions_long (4403843937236648032) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_message_short (1925200607820809677) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_instructions (6902979937802238429) -->
- <skip />
+ <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"No SIM"</string>
+ <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"No SIM in tablet."</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"No SIM in your Android TV device."</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="6184187634180854181">"No SIM in phone."</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="5823469004536805423">"Add a SIM."</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="4403843937236648032">"The SIM is missing or not readable. Add a SIM."</string>
+ <string name="lockscreen_permanent_disabled_sim_message_short" msgid="1925200607820809677">"Unusable SIM."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="6902979937802238429">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Previous track"</string>
<string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Next track"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pause"</string>
@@ -988,13 +978,10 @@
<string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Fast-forward"</string>
<string name="emergency_calls_only" msgid="3057351206678279851">"Emergency calls only"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Network locked"</string>
- <!-- no translation found for lockscreen_sim_puk_locked_message (2867953953604224166) -->
- <skip />
+ <string name="lockscreen_sim_puk_locked_message" msgid="2867953953604224166">"SIM is PUK-locked."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"See the User Guide or contact Customer Care."</string>
- <!-- no translation found for lockscreen_sim_locked_message (5911944931911850164) -->
- <skip />
- <!-- no translation found for lockscreen_sim_unlock_progress_dialog_message (8381565919325410939) -->
- <skip />
+ <string name="lockscreen_sim_locked_message" msgid="5911944931911850164">"SIM is locked."</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"Unlocking SIM…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
@@ -1378,13 +1365,10 @@
<string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"You can change this later in Settings &gt; Apps"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Always Allow*"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Never Allow"</string>
- <!-- no translation found for sim_removed_title (1349026474932481037) -->
- <skip />
- <!-- no translation found for sim_removed_message (8469588437451533845) -->
- <skip />
+ <string name="sim_removed_title" msgid="1349026474932481037">"SIM removed"</string>
+ <string name="sim_removed_message" msgid="8469588437451533845">"The mobile network will be unavailable until you restart with a valid SIM."</string>
<string name="sim_done_button" msgid="6464250841528410598">"Done"</string>
- <!-- no translation found for sim_added_title (2976783426741012468) -->
- <skip />
+ <string name="sim_added_title" msgid="2976783426741012468">"SIM added"</string>
<string name="sim_added_message" msgid="6602906609509958680">"Restart your device to access the mobile network."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Restart"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Activate mobile service"</string>
@@ -1696,8 +1680,7 @@
<string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM is now disabled. Enter PUK code to continue. Contact operator for details."</string>
<string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Enter desired PIN code"</string>
<string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Confirm desired PIN code"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (5743634657721110967) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="5743634657721110967">"Unlocking SIM…"</string>
<string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"Incorrect PIN code."</string>
<string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Type a PIN that is 4 to 8 numbers."</string>
<string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK code should be 8 numbers."</string>
@@ -1754,8 +1737,7 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Turn off Shortcut"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
- <!-- no translation found for color_correction_feature_name (7975133554160979214) -->
- <skip />
+ <string name="color_correction_feature_name" msgid="7975133554160979214">"Colour correction"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-handed mode"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
@@ -2352,4 +2334,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Allows a companion app to deliver companion messages to other devices."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Start foreground services from background"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Allows a companion app to start foreground services from background."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 71c366b9d584..a2ffdb7bed55 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -43,10 +43,8 @@
<string name="mismatchPin" msgid="2929611853228707473">"The PINs you typed don\'t match."</string>
<string name="invalidPin" msgid="7542498253319440408">"Type a PIN that is 4 to 8 numbers."</string>
<string name="invalidPuk" msgid="8831151490931907083">"Type a PUK that is 8 numbers or longer."</string>
- <!-- no translation found for needPuk (3503414069503752211) -->
- <skip />
- <!-- no translation found for needPuk2 (3910763547447344963) -->
- <skip />
+ <string name="needPuk" msgid="3503414069503752211">"Your SIM is PUK-locked. Type the PUK code to unlock it."</string>
+ <string name="needPuk2" msgid="3910763547447344963">"Type PUK2 to unblock SIM."</string>
<string name="enablePin" msgid="2543771964137091212">"Unsuccessful, enable SIM/RUIM Lock."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="other">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
@@ -963,22 +961,14 @@
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Unlock for all features and data"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum Face Unlock attempts exceeded"</string>
- <!-- no translation found for lockscreen_missing_sim_message_short (1229301273156907613) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3986843848305639161) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3903140876952198273) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (6184187634180854181) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions (5823469004536805423) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions_long (4403843937236648032) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_message_short (1925200607820809677) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_instructions (6902979937802238429) -->
- <skip />
+ <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"No SIM"</string>
+ <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"No SIM in tablet."</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"No SIM in your Android TV device."</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="6184187634180854181">"No SIM in phone."</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="5823469004536805423">"Add a SIM."</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="4403843937236648032">"The SIM is missing or not readable. Add a SIM."</string>
+ <string name="lockscreen_permanent_disabled_sim_message_short" msgid="1925200607820809677">"Unusable SIM."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="6902979937802238429">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Previous track"</string>
<string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Next track"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pause"</string>
@@ -988,13 +978,10 @@
<string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Fast forward"</string>
<string name="emergency_calls_only" msgid="3057351206678279851">"Emergency calls only"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Network locked"</string>
- <!-- no translation found for lockscreen_sim_puk_locked_message (2867953953604224166) -->
- <skip />
+ <string name="lockscreen_sim_puk_locked_message" msgid="2867953953604224166">"SIM is PUK-locked."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"See the User Guide or contact Customer Care."</string>
- <!-- no translation found for lockscreen_sim_locked_message (5911944931911850164) -->
- <skip />
- <!-- no translation found for lockscreen_sim_unlock_progress_dialog_message (8381565919325410939) -->
- <skip />
+ <string name="lockscreen_sim_locked_message" msgid="5911944931911850164">"SIM is locked."</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"Unlocking SIM…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
@@ -1378,13 +1365,10 @@
<string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"You can change this later in Settings &gt; Apps"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Always allow"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Never Allow"</string>
- <!-- no translation found for sim_removed_title (1349026474932481037) -->
- <skip />
- <!-- no translation found for sim_removed_message (8469588437451533845) -->
- <skip />
+ <string name="sim_removed_title" msgid="1349026474932481037">"SIM removed"</string>
+ <string name="sim_removed_message" msgid="8469588437451533845">"The mobile network will be unavailable until you restart with a valid SIM."</string>
<string name="sim_done_button" msgid="6464250841528410598">"Done"</string>
- <!-- no translation found for sim_added_title (2976783426741012468) -->
- <skip />
+ <string name="sim_added_title" msgid="2976783426741012468">"SIM added"</string>
<string name="sim_added_message" msgid="6602906609509958680">"Restart your device to access the mobile network."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Restart"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Activate mobile service"</string>
@@ -1696,8 +1680,7 @@
<string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM is now disabled. Enter PUK code to continue. Contact carrier for details."</string>
<string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Enter desired PIN code"</string>
<string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Confirm desired PIN code"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (5743634657721110967) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="5743634657721110967">"Unlocking SIM…"</string>
<string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"Incorrect PIN code."</string>
<string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Type a PIN that is 4 to 8 numbers."</string>
<string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK code should be 8 numbers."</string>
@@ -1754,8 +1737,7 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Turn off Shortcut"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour inversion"</string>
- <!-- no translation found for color_correction_feature_name (7975133554160979214) -->
- <skip />
+ <string name="color_correction_feature_name" msgid="7975133554160979214">"Color correction"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-Handed mode"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
@@ -2352,4 +2334,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Allows a companion app to deliver companion messages to other devices."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Start foreground services from background"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Allows a companion app to start foreground services from background."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 1658c33aee73..32774df6f832 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -43,10 +43,8 @@
<string name="mismatchPin" msgid="2929611853228707473">"The PINs that you typed don\'t match."</string>
<string name="invalidPin" msgid="7542498253319440408">"Type a PIN that is 4 to 8 numbers."</string>
<string name="invalidPuk" msgid="8831151490931907083">"Type a PUK that is 8 numbers or longer."</string>
- <!-- no translation found for needPuk (3503414069503752211) -->
- <skip />
- <!-- no translation found for needPuk2 (3910763547447344963) -->
- <skip />
+ <string name="needPuk" msgid="3503414069503752211">"Your SIM is PUK-locked. Type the PUK code to unlock it."</string>
+ <string name="needPuk2" msgid="3910763547447344963">"Type PUK2 to unblock SIM."</string>
<string name="enablePin" msgid="2543771964137091212">"Unsuccessful, enable SIM/RUIM Lock."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="other">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
@@ -963,22 +961,14 @@
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Unlock for all features and data"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum Face Unlock attempts exceeded"</string>
- <!-- no translation found for lockscreen_missing_sim_message_short (1229301273156907613) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3986843848305639161) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3903140876952198273) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (6184187634180854181) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions (5823469004536805423) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions_long (4403843937236648032) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_message_short (1925200607820809677) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_instructions (6902979937802238429) -->
- <skip />
+ <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"No SIM"</string>
+ <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"No SIM in tablet."</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"No SIM in your Android TV device."</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="6184187634180854181">"No SIM in phone."</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="5823469004536805423">"Add a SIM."</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="4403843937236648032">"The SIM is missing or not readable. Add a SIM."</string>
+ <string name="lockscreen_permanent_disabled_sim_message_short" msgid="1925200607820809677">"Unusable SIM."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="6902979937802238429">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Previous track"</string>
<string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Next track"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pause"</string>
@@ -988,13 +978,10 @@
<string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Fast-forward"</string>
<string name="emergency_calls_only" msgid="3057351206678279851">"Emergency calls only"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Network locked"</string>
- <!-- no translation found for lockscreen_sim_puk_locked_message (2867953953604224166) -->
- <skip />
+ <string name="lockscreen_sim_puk_locked_message" msgid="2867953953604224166">"SIM is PUK-locked."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"See the User Guide or contact Customer Care."</string>
- <!-- no translation found for lockscreen_sim_locked_message (5911944931911850164) -->
- <skip />
- <!-- no translation found for lockscreen_sim_unlock_progress_dialog_message (8381565919325410939) -->
- <skip />
+ <string name="lockscreen_sim_locked_message" msgid="5911944931911850164">"SIM is locked."</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"Unlocking SIM…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
@@ -1378,13 +1365,10 @@
<string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"You can change this later in Settings &gt; Apps"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Always Allow*"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Never Allow"</string>
- <!-- no translation found for sim_removed_title (1349026474932481037) -->
- <skip />
- <!-- no translation found for sim_removed_message (8469588437451533845) -->
- <skip />
+ <string name="sim_removed_title" msgid="1349026474932481037">"SIM removed"</string>
+ <string name="sim_removed_message" msgid="8469588437451533845">"The mobile network will be unavailable until you restart with a valid SIM."</string>
<string name="sim_done_button" msgid="6464250841528410598">"Done"</string>
- <!-- no translation found for sim_added_title (2976783426741012468) -->
- <skip />
+ <string name="sim_added_title" msgid="2976783426741012468">"SIM added"</string>
<string name="sim_added_message" msgid="6602906609509958680">"Restart your device to access the mobile network."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Restart"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Activate mobile service"</string>
@@ -1696,8 +1680,7 @@
<string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM is now disabled. Enter PUK code to continue. Contact operator for details."</string>
<string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Enter desired PIN code"</string>
<string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Confirm desired PIN code"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (5743634657721110967) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="5743634657721110967">"Unlocking SIM…"</string>
<string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"Incorrect PIN code."</string>
<string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Type a PIN that is 4 to 8 numbers."</string>
<string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK code should be 8 numbers."</string>
@@ -1754,8 +1737,7 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Turn off Shortcut"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
- <!-- no translation found for color_correction_feature_name (7975133554160979214) -->
- <skip />
+ <string name="color_correction_feature_name" msgid="7975133554160979214">"Colour correction"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-handed mode"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
@@ -2352,4 +2334,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Allows a companion app to deliver companion messages to other devices."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Start foreground services from background"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Allows a companion app to start foreground services from background."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 182faa166a3e..f742427cf034 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -43,10 +43,8 @@
<string name="mismatchPin" msgid="2929611853228707473">"The PINs that you typed don\'t match."</string>
<string name="invalidPin" msgid="7542498253319440408">"Type a PIN that is 4 to 8 numbers."</string>
<string name="invalidPuk" msgid="8831151490931907083">"Type a PUK that is 8 numbers or longer."</string>
- <!-- no translation found for needPuk (3503414069503752211) -->
- <skip />
- <!-- no translation found for needPuk2 (3910763547447344963) -->
- <skip />
+ <string name="needPuk" msgid="3503414069503752211">"Your SIM is PUK-locked. Type the PUK code to unlock it."</string>
+ <string name="needPuk2" msgid="3910763547447344963">"Type PUK2 to unblock SIM."</string>
<string name="enablePin" msgid="2543771964137091212">"Unsuccessful, enable SIM/RUIM Lock."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="other">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
@@ -963,22 +961,14 @@
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Unlock for all features and data"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum Face Unlock attempts exceeded"</string>
- <!-- no translation found for lockscreen_missing_sim_message_short (1229301273156907613) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3986843848305639161) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3903140876952198273) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (6184187634180854181) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions (5823469004536805423) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions_long (4403843937236648032) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_message_short (1925200607820809677) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_instructions (6902979937802238429) -->
- <skip />
+ <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"No SIM"</string>
+ <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"No SIM in tablet."</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"No SIM in your Android TV device."</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="6184187634180854181">"No SIM in phone."</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="5823469004536805423">"Add a SIM."</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="4403843937236648032">"The SIM is missing or not readable. Add a SIM."</string>
+ <string name="lockscreen_permanent_disabled_sim_message_short" msgid="1925200607820809677">"Unusable SIM."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="6902979937802238429">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Previous track"</string>
<string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Next track"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pause"</string>
@@ -988,13 +978,10 @@
<string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Fast-forward"</string>
<string name="emergency_calls_only" msgid="3057351206678279851">"Emergency calls only"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Network locked"</string>
- <!-- no translation found for lockscreen_sim_puk_locked_message (2867953953604224166) -->
- <skip />
+ <string name="lockscreen_sim_puk_locked_message" msgid="2867953953604224166">"SIM is PUK-locked."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"See the User Guide or contact Customer Care."</string>
- <!-- no translation found for lockscreen_sim_locked_message (5911944931911850164) -->
- <skip />
- <!-- no translation found for lockscreen_sim_unlock_progress_dialog_message (8381565919325410939) -->
- <skip />
+ <string name="lockscreen_sim_locked_message" msgid="5911944931911850164">"SIM is locked."</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"Unlocking SIM…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
@@ -1378,13 +1365,10 @@
<string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"You can change this later in Settings &gt; Apps"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Always Allow*"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Never Allow"</string>
- <!-- no translation found for sim_removed_title (1349026474932481037) -->
- <skip />
- <!-- no translation found for sim_removed_message (8469588437451533845) -->
- <skip />
+ <string name="sim_removed_title" msgid="1349026474932481037">"SIM removed"</string>
+ <string name="sim_removed_message" msgid="8469588437451533845">"The mobile network will be unavailable until you restart with a valid SIM."</string>
<string name="sim_done_button" msgid="6464250841528410598">"Done"</string>
- <!-- no translation found for sim_added_title (2976783426741012468) -->
- <skip />
+ <string name="sim_added_title" msgid="2976783426741012468">"SIM added"</string>
<string name="sim_added_message" msgid="6602906609509958680">"Restart your device to access the mobile network."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Restart"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Activate mobile service"</string>
@@ -1696,8 +1680,7 @@
<string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM is now disabled. Enter PUK code to continue. Contact operator for details."</string>
<string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Enter desired PIN code"</string>
<string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Confirm desired PIN code"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (5743634657721110967) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="5743634657721110967">"Unlocking SIM…"</string>
<string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"Incorrect PIN code."</string>
<string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Type a PIN that is 4 to 8 numbers."</string>
<string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK code should be 8 numbers."</string>
@@ -1754,8 +1737,7 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Turn off Shortcut"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
- <!-- no translation found for color_correction_feature_name (7975133554160979214) -->
- <skip />
+ <string name="color_correction_feature_name" msgid="7975133554160979214">"Colour correction"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-handed mode"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
@@ -2352,4 +2334,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Allows a companion app to deliver companion messages to other devices."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Start foreground services from background"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Allows a companion app to start foreground services from background."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 39f97157c61f..5a997be8da80 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -43,10 +43,8 @@
<string name="mismatchPin" msgid="2929611853228707473">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎The PINs you typed don\'t match.‎‏‎‎‏‎"</string>
<string name="invalidPin" msgid="7542498253319440408">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎Type a PIN that is 4 to 8 numbers.‎‏‎‎‏‎"</string>
<string name="invalidPuk" msgid="8831151490931907083">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎Type a PUK that is 8 numbers or longer.‎‏‎‎‏‎"</string>
- <!-- no translation found for needPuk (3503414069503752211) -->
- <skip />
- <!-- no translation found for needPuk2 (3910763547447344963) -->
- <skip />
+ <string name="needPuk" msgid="3503414069503752211">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎Your SIM is PUK-locked. Type the PUK code to unlock it.‎‏‎‎‏‎"</string>
+ <string name="needPuk2" msgid="3910763547447344963">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎Type PUK2 to unblock SIM.‎‏‎‎‏‎"</string>
<string name="enablePin" msgid="2543771964137091212">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎Unsuccessful, enable SIM/RUIM Lock.‎‏‎‎‏‎"</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎You have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempts before SIM is locked.‎‏‎‎‏‎</item>
@@ -963,22 +961,14 @@
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎Try again‎‏‎‎‏‎"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎Unlock for all features and data‎‏‎‎‏‎"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎Maximum Face Unlock attempts exceeded‎‏‎‎‏‎"</string>
- <!-- no translation found for lockscreen_missing_sim_message_short (1229301273156907613) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3986843848305639161) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (3903140876952198273) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_message (6184187634180854181) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions (5823469004536805423) -->
- <skip />
- <!-- no translation found for lockscreen_missing_sim_instructions_long (4403843937236648032) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_message_short (1925200607820809677) -->
- <skip />
- <!-- no translation found for lockscreen_permanent_disabled_sim_instructions (6902979937802238429) -->
- <skip />
+ <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‎‏‎No SIM‎‏‎‎‏‎"</string>
+ <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎No SIM in tablet.‎‏‎‎‏‎"</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‎No SIM in your Android TV device.‎‏‎‎‏‎"</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="6184187634180854181">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‎‏‏‎‏‎‎‏‎‏‎No SIM in phone.‎‏‎‎‏‎"</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="5823469004536805423">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎Add a SIM.‎‏‎‎‏‎"</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="4403843937236648032">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‏‎‎‎‎‎‎The SIM is missing or not readable. Add a SIM.‎‏‎‎‏‎"</string>
+ <string name="lockscreen_permanent_disabled_sim_message_short" msgid="1925200607820809677">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎Unusable SIM.‎‏‎‎‏‎"</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="6902979937802238429">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‏‎‏‎Your SIM has been permanently deactivated.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ Contact your wireless service provider for another SIM.‎‏‎‎‏‎"</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎Previous track‎‏‎‎‏‎"</string>
<string name="lockscreen_transport_next_description" msgid="2931509904881099919">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎‎‎‏‏‏‏‎Next track‎‏‎‎‏‎"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎Pause‎‏‎‎‏‎"</string>
@@ -988,13 +978,10 @@
<string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‎‎‎Fast forward‎‏‎‎‏‎"</string>
<string name="emergency_calls_only" msgid="3057351206678279851">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎Emergency calls only‎‏‎‎‏‎"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‎Network locked‎‏‎‎‏‎"</string>
- <!-- no translation found for lockscreen_sim_puk_locked_message (2867953953604224166) -->
- <skip />
+ <string name="lockscreen_sim_puk_locked_message" msgid="2867953953604224166">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎SIM is PUK-locked.‎‏‎‎‏‎"</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎See the User Guide or contact Customer Care.‎‏‎‎‏‎"</string>
- <!-- no translation found for lockscreen_sim_locked_message (5911944931911850164) -->
- <skip />
- <!-- no translation found for lockscreen_sim_unlock_progress_dialog_message (8381565919325410939) -->
- <skip />
+ <string name="lockscreen_sim_locked_message" msgid="5911944931911850164">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎‎SIM is locked.‎‏‎‎‏‎"</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‏‏‏‏‎‏‏‎Unlocking SIM…‎‏‎‎‏‎"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‎‎‎‎‎‎You have incorrectly drawn your unlock pattern ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ times. ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try again in ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ seconds.‎‏‎‎‏‎"</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎You have incorrectly typed your password ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ times. ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try again in ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ seconds.‎‏‎‎‏‎"</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‎‎‎‎You have incorrectly typed your PIN ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ times. ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try again in ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ seconds.‎‏‎‎‏‎"</string>
@@ -1378,13 +1365,10 @@
<string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎‎You can change this later in Settings &gt; Apps‎‏‎‎‏‎"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‎Always Allow‎‏‎‎‏‎"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‎‏‏‏‏‎Never Allow‎‏‎‎‏‎"</string>
- <!-- no translation found for sim_removed_title (1349026474932481037) -->
- <skip />
- <!-- no translation found for sim_removed_message (8469588437451533845) -->
- <skip />
+ <string name="sim_removed_title" msgid="1349026474932481037">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‏‎SIM removed‎‏‎‎‏‎"</string>
+ <string name="sim_removed_message" msgid="8469588437451533845">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‎The mobile network will be unavailable until you restart with a valid SIM.‎‏‎‎‏‎"</string>
<string name="sim_done_button" msgid="6464250841528410598">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎Done‎‏‎‎‏‎"</string>
- <!-- no translation found for sim_added_title (2976783426741012468) -->
- <skip />
+ <string name="sim_added_title" msgid="2976783426741012468">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎SIM added‎‏‎‎‏‎"</string>
<string name="sim_added_message" msgid="6602906609509958680">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎Restart your device to access the mobile network.‎‏‎‎‏‎"</string>
<string name="sim_restart_button" msgid="8481803851341190038">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎Restart‎‏‎‎‏‎"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎Activate mobile service‎‏‎‎‏‎"</string>
@@ -1696,8 +1680,7 @@
<string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎SIM is now disabled. Enter PUK code to continue. Contact carrier for details.‎‏‎‎‏‎"</string>
<string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎Enter desired PIN code‎‏‎‎‏‎"</string>
<string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎Confirm desired PIN code‎‏‎‎‏‎"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (5743634657721110967) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="5743634657721110967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎Unlocking SIM…‎‏‎‎‏‎"</string>
<string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎Incorrect PIN code.‎‏‎‎‏‎"</string>
<string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎Type a PIN that is 4 to 8 numbers.‎‏‎‎‏‎"</string>
<string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‏‎PUK code should be 8 numbers.‎‏‎‎‏‎"</string>
@@ -1754,8 +1737,7 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎Turn off Shortcut‎‏‎‎‏‎"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎Use Shortcut‎‏‎‎‏‎"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎Color Inversion‎‏‎‎‏‎"</string>
- <!-- no translation found for color_correction_feature_name (7975133554160979214) -->
- <skip />
+ <string name="color_correction_feature_name" msgid="7975133554160979214">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‎Color correction‎‏‎‎‏‎"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎One-Handed mode‎‏‎‎‏‎"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‎‎‎Extra dim‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎Held volume keys. ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ turned on.‎‏‎‎‏‎"</string>
@@ -2352,4 +2334,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎Allows a companion app to deliver companion messages to other devices.‎‏‎‎‏‎"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎Start foreground services from background‎‏‎‎‏‎"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‎‎‎‎‎‎‎‎‎‏‎Allows a companion app to start foreground services from background.‎‏‎‎‏‎"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 8608754dfd42..52053e879653 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Permite que una aplicación complementaria envíe mensajes complementarios a otros dispositivos."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Comenzar servicios en primer plano desde el segundo plano"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permite que una aplicación complementaria inicie servicios en primer plano desde el segundo plano."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 86837362b8a5..639ef0b6e269 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Permite que una aplicación complementaria envíe mensajes complementarios a otros dispositivos."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Iniciar servicios en primer plano desde el segundo plano"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permite que una aplicación complementaria inicie servicios en primer plano desde el segundo plano."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index d469e40e227b..210b50918e89 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Lubab kaasrakendusel teistesse seadmetesse kaassõnumeid toimetada."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Esiplaanil olevate teenuste käivitamine taustal"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Lubab kaasrakendusel taustal käivitada esiplaanil olevaid teenuseid."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index ee30c2d2f838..c60da1df2cc6 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Beste gailuetan mezuak entregatzeko baimena ematen die aplikazio osagarriei."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"hasi aurreko planoko zerbitzuak atzeko planotik"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Aurreko planoko zerbitzuak atzeko planotik abiarazteko baimena ematen die aplikazio osagarriei."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index ed5a2ac1a4ea..670ab452e18a 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"به برنامه همراه اجازه می‌دهد پیام‌های همراه را به دستگاه‌های دیگر ارسال کند."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"اجرای سرویس‌های پیش‌نما از پس‌زمینه"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"به برنامه همراه اجازه می‌دهد سرویس‌های پیش‌نما را از پس‌زمینه راه‌اندازی کند."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index c1bf4c6f31fd..df6f15b11c34 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Sallii kumppanisovelluksen toimittaa kumppaniviestejä muille laitteille."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Etualan palvelujen aloittaminen taustalla"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Sallii kumppanisovelluksen aloittaa etualan palveluja taustalla."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index bc2130cb39ba..0a7ce08851a1 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Autorise une application compagnon à transmettre des messages à d\'autres appareils."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Lancer les services d\'avant-plan à partir de l\'arrière-plan"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permet à une application compagnon en arrière-plan de lancer des services d\'avant-plan."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 0e76f98c7402..bb449b0e5faa 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Autorise une application associée à transmettre des messages associés à d\'autres appareils."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Lancer des services de premier plan à partir de l\'arrière-plan"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Autorise une application associée à lancer des services de premier plan à partir de l\'arrière-plan."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 7aeeea064d2c..7628658756c8 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Permite que unha aplicación complementaria envíe mensaxes complementarias a outros dispositivos."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Desde un segundo plano iniciar servizos en primeiro plano"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permite que nun segundo plano unha aplicación complementaria inicie servizos en primeiro plano."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index de3f8ae17590..4cae4e53cc42 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"અન્ય ડિવાઇસ પર સાથી મેસેજ ડિલિવર કરવા માટે સાથી ઍપને મંજૂરી આપે છે."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"બૅકગ્રાઉન્ડમાંથી ફૉરગ્રાઉન્ડ સેવાઓ શરૂ કરો"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"સાથી ઍપને બૅકગ્રાઉન્ડમાંથી ફૉરગ્રાઉન્ડ સેવાઓ શરૂ કરવાની મંજૂરી આપે છે."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 31518e9fac4c..fb4af5de62de 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"इससे साथी ऐप्लिकेशन को अन्य डिवाइसों पर, साथी ऐप्लिकेशन के मैसेज डिलीवर करने की अनुमति मिलती है."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"बैकग्राउंड में फ़ोरग्राउंड सेवाएं चलाने की अनुमति दें"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"इससे साथी ऐप्लिकेशन को बैकग्राउंड में फ़ोरग्राउंड सेवाएं चलाने की अनुमति मिलती है."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e057dd325985..02338fbf497f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Popratnoj aplikaciji omogućuje isporuku popratnih poruka drugim uređajima."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Pokreni usluge u prednjem planu iz pozadine"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Popratnoj aplikaciji omogućuje da iz pozadine pokrene usluge u prednjem planu."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 1d9da72d40d4..cb497f4ba166 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Lehetővé teszi a társalkalmazások számára, hogy társüzeneteket küldjenek más eszközökre."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Előtérben futó szolgáltatások indítása a háttérből"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Lehetővé teszi a társalkalmazások számára, hogy előtérben futó szolgáltatásokat indítsanak a háttérből."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index bf2120a813cd..89f60278a339 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Թույլատրում է ուղեկցող հավելվածին հաղորդագրություններ առաքել այլ սարքեր։"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Ակտիվ ծառայությունների գործարկում ֆոնային ռեժիմից"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Թույլատրում է ուղեկցող հավելվածին ակտիվ ծառայություններ գործարկել ֆոնային ռեժիմից։"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index d4046eb9023f..b3bece9d65e4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Mengizinkan aplikasi pendamping mengirimkan pesan pendamping ke perangkat lain."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Memulai layanan latar depan dari latar belakang"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Mengizinkan aplikasi pendamping memulai layanan latar depan dari latar belakang."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 7c6e719b5631..4e44ba5adb09 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Leyfir fylgiforriti að senda fylgiskilaboð til annarra tækja."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Ræsa forgrunnsþjónustur úr bakgrunni"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Leyfir fylgiforriti að ræsa forgrunnsþjónustur úr bakgrunni."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index a4e0c17b1a89..ee704abb3673 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Consente a un\'app complementare di consegnare messaggi complementari ad altri dispositivi."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Avviare i servizi in primo piano dal background"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Consente a un\'app complementare di avviare servizi in primo piano dal background."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index dda48df3774d..3480e98cc869 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"ההרשאה הזו מאפשרת לאפליקציה נלווית להעביר הודעות נלוות למכשירים אחרים."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"הפעלה מהרקע של שירותים שפועלים בחזית"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ההרשאה הזו מאפשרת לאפליקציה נלווית להפעיל מהרקע שירותים שפועלים בחזית."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 0bd593be861f..d626dae7a98b 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"他のデバイスへのコンパニオン メッセージの配信をコンパニオン アプリに許可します。"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"バックグラウンドからのフォアグラウンド サービスの起動"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"バックグラウンドからのフォアグラウンド サービスの起動をコンパニオン アプリに許可します。"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index ea000b21f5f9..5af4fb40a04b 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"კომპანიონ აპს საშუალებას აძლევს, რომ მიაწოდოს გზავნილები სხვა მოწყობილობებზე."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"უპირატესი სერვისების ფონური რეჟიმიდან გაშვება"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"საშუალებას აძლევს კომპანიონ აპს, რომ გაუშვას უპირატესი სერვისები ფონური რეჟიმიდან."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 8ffd05e34f36..6c2245cad29d 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Қосымша қолданбаға ілеспе хабарларды басқа құрылғыларға жеткізуге рұқсат беріледі."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Экрандық режимдегі қызметтерді фоннан іске қосу"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Қосымша қолданбаға экрандық режимдегі қызметтерді фоннан іске қосуға рұқсат беріледі."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 45377352624d..dd1a3ead38d1 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"អនុញ្ញាតឱ្យកម្មវិធីដៃគូបញ្ជូនសារដៃគូទៅកាន់ឧបករណ៍ផ្សេងទៀត។"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"ចាប់ផ្តើមសេវាកម្មផ្ទៃខាងមុខពីផ្ទៃខាងក្រោយ"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"អនុញ្ញាតឱ្យកម្មវិធីដៃគូចាប់ផ្តើមសេវាកម្មផ្ទៃខាងមុខពីផ្ទៃខាងក្រោយ។"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 2817b36644fd..be4c5bc8b847 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"ಇತರ ಸಾಧನಗಳಿಗೆ ಕಂಪ್ಯಾನಿಯನ್ ಸಂದೇಶಗಳನ್ನು ತಲುಪಿಸಲು ಕಂಪ್ಯಾನಿಯನ್ ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"ಮುನ್ನೆಲೆ ಸೇವೆಗಳನ್ನು ಹಿನ್ನೆಲೆಯಿಂದ ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ಮುನ್ನೆಲೆ ಸೇವೆಗಳನ್ನು ಹಿನ್ನೆಲೆಯಿಂದ ಪ್ರಾರಂಭಿಸಲು ಕಂಪ್ಯಾನಿಯನ್ ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 3727fab8d032..5c964878dfc6 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"호환 앱에서 호환 메시지를 다른 기기로 전달하도록 허용합니다."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"백그라운드에서 포그라운드 서비스 시작"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"호환 앱이 백그라운드에서 포그라운드 서비스를 시작할 수 있게 허용합니다."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 06db92d175bc..c9b7c518ff76 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Көмөкчү колдонмого билдирүүлөрдү башка түзмөктөргө жөнөтүүгө уруксат берет."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Активдүү кызматтарды фондо иштетүү"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Көмөкчү колдонмого активдүү кызматтарды фондо иштетүүгө уруксат берет."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index d1650bbd4e30..318945b2d8a4 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"ອະນຸຍາດໃຫ້ແອັບຊ່ວຍເຫຼືອສົ່ງຂໍ້ຄວາມຊ່ວຍເຫຼືອໄປຫາອຸປະກອນອື່ນໆ."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"ເລີ່ມໃຊ້ບໍລິການທີ່ເຮັດວຽກຢູ່ເບື້ອງໜ້າໂດຍໃຫ້ອະນຸຍາດຈາກເບື້ອງຫຼັງ"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ອະນຸຍາດຈາກເບື້ອງຫຼັງໃຫ້ແອັບຊ່ວຍເຫຼືອເລີ່ມໃຊ້ບໍລິການທີ່ເຮັດວຽກຢູ່ເບື້ອງໜ້າ."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 47fd09b2f3c6..688e881f25bc 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -2354,4 +2354,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Leidžiama papildomai programai teikti papildomos programos pranešimus į kitus įrenginius."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Priekinio plano paslaugų paleidimas fone"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Leidžiama papildomai programai paleisti priekinio plano paslaugas fone."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 39fd849316bd..8b9f27f64920 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Ļauj palīglietotnei piegādāt palīgziņojumus citām ierīcēm."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Sākt priekšplāna pakalpojumus no fona"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Ļauj palīglietotnei sākt priekšplāna pakalpojumus no fona."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mcc001-mnc01-b+sr+Latn/strings.xml b/core/res/res/values-mcc001-mnc01-b+sr+Latn/strings.xml
index 7c0fc701125c..b003fe0575ff 100644
--- a/core/res/res/values-mcc001-mnc01-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc001-mnc01-b+sr+Latn/strings.xml
@@ -20,5 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_illegal_me" msgid="6819499009131365312">"Telefon nije dozvoljen MM#6"</string>
+ <string name="mmcc_illegal_me" msgid="6819499009131365312">"Телефон није дозвољен MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc150-b+sr+Latn/strings.xml b/core/res/res/values-mcc310-mnc150-b+sr+Latn/strings.xml
index d066eb62db69..c70f4b781746 100644
--- a/core/res/res/values-mcc310-mnc150-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc310-mnc150-b+sr+Latn/strings.xml
@@ -20,5 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_illegal_me" msgid="8004509200390992737">"Telefon nije dozvoljen MM#6"</string>
+ <string name="mmcc_illegal_me" msgid="8004509200390992737">"Телефон није дозвољен MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc170-b+sr+Latn/strings.xml b/core/res/res/values-mcc310-mnc170-b+sr+Latn/strings.xml
index ff89baba6a74..011a40d33e29 100644
--- a/core/res/res/values-mcc310-mnc170-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc310-mnc170-b+sr+Latn/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="5424518490295341205">"SIM kartica nije podešena MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="3527626511418944853">"SIM kartica nije dozvoljena MM#3"</string>
- <string name="mmcc_illegal_me" msgid="3948912590657398489">"Telefon nije dozvoljen MM#6"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5424518490295341205">"SIM картица није подешена MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="3527626511418944853">"SIM картица није дозвољена MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="3948912590657398489">"Телефон није дозвољен MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc280-b+sr+Latn/strings.xml b/core/res/res/values-mcc310-mnc280-b+sr+Latn/strings.xml
index faf51d3e95d4..9b0dcf18cfa3 100644
--- a/core/res/res/values-mcc310-mnc280-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc310-mnc280-b+sr+Latn/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="1070849538022865416">"SIM kartica nije podešena MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="499832197298480670">"SIM kartica nije dozvoljena MM#3"</string>
- <string name="mmcc_illegal_me" msgid="2346111479504469688">"Telefon nije dozvoljen MM#6"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="1070849538022865416">"SIM картица није подешена MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="499832197298480670">"SIM картица није дозвољена MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="2346111479504469688">"Телефон није дозвољен MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc380-b+sr+Latn/strings.xml b/core/res/res/values-mcc310-mnc380-b+sr+Latn/strings.xml
index e3480952396f..256ad83be8fc 100644
--- a/core/res/res/values-mcc310-mnc380-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc310-mnc380-b+sr+Latn/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6178029798083341927">"SIM kartica nije podešena MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="6084322234976891423">"SIM kartica nije dozvoljena MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6178029798083341927">"SIM картица није подешена MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="6084322234976891423">"SIM картица није дозвољена MM#3"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc410-b+sr+Latn/strings.xml b/core/res/res/values-mcc310-mnc410-b+sr+Latn/strings.xml
index 940507b969be..e8835d2f881a 100644
--- a/core/res/res/values-mcc310-mnc410-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc310-mnc410-b+sr+Latn/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="8861901652350883183">"SIM kartica nije podešena MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="2604694337529846283">"SIM kartica nije dozvoljena MM#3"</string>
- <string name="mmcc_illegal_me" msgid="3099618295079374317">"Telefon nije dozvoljen MM#6"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="8861901652350883183">"SIM картица није подешена MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="2604694337529846283">"SIM картица није дозвољена MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="3099618295079374317">"Телефон није дозвољен MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc950-b+sr+Latn/strings.xml b/core/res/res/values-mcc310-mnc950-b+sr+Latn/strings.xml
index cdf92ca30153..9005aaf2689c 100644
--- a/core/res/res/values-mcc310-mnc950-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc310-mnc950-b+sr+Latn/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="615419724607901560">"SIM kartica nije podešena MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="7801541624846497489">"SIM kartica nije dozvoljena MM#3"</string>
- <string name="mmcc_illegal_me" msgid="7066936962628406316">"Telefon nije dozvoljen MM#6"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="615419724607901560">"SIM картица није подешена MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="7801541624846497489">"SIM картица није дозвољена MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="7066936962628406316">"Телефон није дозвољен MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc180-b+sr+Latn/strings.xml b/core/res/res/values-mcc311-mnc180-b+sr+Latn/strings.xml
index 6cbc6ee49930..baec8cd251dd 100644
--- a/core/res/res/values-mcc311-mnc180-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc311-mnc180-b+sr+Latn/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM kartica nije podešena MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM kartica nije dozvoljena MM#3"</string>
- <string name="mmcc_illegal_me" msgid="4936539345546223576">"Telefon nije dozvoljen MM#6"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM картица није подешена MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM картица није дозвољена MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Телефон није дозвољен MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc312-mnc670-b+sr+Latn/strings.xml b/core/res/res/values-mcc312-mnc670-b+sr+Latn/strings.xml
index d9b679355c17..6c738a20f34d 100644
--- a/core/res/res/values-mcc312-mnc670-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc312-mnc670-b+sr+Latn/strings.xml
@@ -20,5 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_illegal_me" msgid="8271599016773350087">"Telefon nije dozvoljen MM#6"</string>
+ <string name="mmcc_illegal_me" msgid="8271599016773350087">"Телефон није дозвољен MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc313-mnc100-b+sr+Latn/strings.xml b/core/res/res/values-mcc313-mnc100-b+sr+Latn/strings.xml
index 4740afd98067..1ff6057d1624 100644
--- a/core/res/res/values-mcc313-mnc100-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc313-mnc100-b+sr+Latn/strings.xml
@@ -20,5 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_illegal_me" msgid="8297733805803070310">"Telefon nije dozvoljen MM#6"</string>
+ <string name="mmcc_illegal_me" msgid="8297733805803070310">"Телефон није дозвољен MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml b/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml
index b27e485e5f73..ddf0ce15aa78 100644
--- a/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml
+++ b/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
- <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"POTVRDA IDENTITETA NIJE USPELA -29-"</string>
- <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NISTE PRETPLAĆENI NA USLUGU -33-"</string>
- <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Nije dozvoljeno više PDN veza za određeni APN -55-"</string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ПОТВРДА ИДЕНТИТЕТА НИЈЕ УСПЕЛА -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НИСТЕ ПРЕТПЛАЋЕНИ НА УСЛУГУ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Није дозвољено више PDN веза за одређени APN -55-"</string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 1f85755b0937..b5ba29c87a17 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Дозволува придружна апликација да доставува придружни пораки до други уреди."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Стартување услуги во преден план од заднина"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Дозволува придружна апликација да започне услуги во преден план од заднината."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 1ebdcba4c637..2001f92048e0 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"മറ്റ് ഉപകരണങ്ങളിലേക്ക് സഹകാരി സന്ദേശങ്ങൾ ഡെലിവർ ചെയ്യാൻ സഹകാരി ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"പശ്ചാത്തലത്തിൽ നിന്ന് ഫോർഗ്രൗണ്ട് സേവനങ്ങൾ ആരംഭിക്കുക"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"പശ്ചാത്തലത്തിൽ നിന്ന് ഫോർഗ്രൗണ്ട് സേവനങ്ങൾ ആരംഭിക്കാൻ സഹകാരി ആപ്പിനെ അനുവദിക്കുന്നു."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 45129103c115..a060109bb8c0 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Дэмжигч аппад бусад төхөөрөмжид дэмжигчийн мессежийг хүргэхийг зөвшөөрнө."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Нүүрэн талын үйлчилгээнүүдийг ардаас эхлүүлэх"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Дэмжигч аппад нүүрэн талын үйлчилгээнүүдийг ардаас эхлүүлэхийг зөвшөөрнө."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 784d4e3136bf..25b5b92da6ed 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"सहयोगी अ‍ॅपला इतर डिव्हाइसवर सहयोगी मेसेज डिलिव्हर करण्याची अनुमती देते."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"बॅकग्राउंडमधून फोरग्राउंड सेवा सुरू करा"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"सहयोगी अ‍ॅपला बॅकग्राउंडमधून फोरग्राउंड सेवा सुरू करण्याची अनुमती देते."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 7ff53e83b2b3..e71f8e2dcc08 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Benarkan apl rakan menghantar mesej rakan kepada peranti lain."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Mulakan perkhidmatan latar depan dari latar"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Benarkan apl rakan memulakan perkhidmatan latar depan dari latar."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index aac7e3b354db..789ac5be2728 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"အခြားစက်များသို့ တွဲသုံးနိုင်သော မက်ဆေ့ဂျ်များပို့ရန် တွဲဖက် အက်ပ်ကို ခွင့်ပြုသည်။"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"နောက်ခံမှနေ၍ မျက်နှာစာဝန်ဆောင်မှုများ စတင်ခြင်း"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"နောက်ခံမှနေ၍ မျက်နှာစာဝန်ဆောင်မှုများ စတင်ရန် တွဲဖက် အက်ပ်ကို ခွင့်ပြုသည်။"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 3f7f9b025d4b..ba8f47012200 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Lar en følgeapp levere meldinger til andre enheter."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Start forgrunnstjenester fra bakgrunnen"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Lar en følgeapp starte forgrunnstjenester fra bakgrunnen."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 31250f47d662..9760944cc76f 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"यसले सहयोगी एपलाई अन्य डिभाइसमा सहयोगी म्यासेजहरू डेलिभर गर्ने अनुमति दिन्छ।"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"ब्याकग्राउन्डमा फोरग्राउन्ड सेवाहरू चलाउने अनुमति"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"यसले सहयोगी एपलाई ब्याकग्राउन्डमा फोरग्राउन्ड सेवाहरू चलाउने अनुमति दिन्छ।"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index ed1856720221..9334303b495f 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Hiermee kan een bijbehorende app berichten naar andere apparaten sturen."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Service op de voorgrond vanuit de achtergrond starten"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Hiermee kan een bijbehorende app services op de voorgrond vanuit de achtergrond starten."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 611421ffd0ed..2dd316c90259 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"ଅନ୍ୟ ଡିଭାଇସଗୁଡ଼ିକରେ ସହଯୋଗୀ ମେସେଜଗୁଡ଼ିକ ଡେଲିଭର କରିବା ପାଇଁ ଏକ ସହଯୋଗୀ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"ପୃଷ୍ଠପଟରୁ ଫୋରଗ୍ରାଉଣ୍ଡ ସେବାଗୁଡ଼ିକ ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ପୃଷ୍ଠପଟରୁ ଫୋରଗ୍ରାଉଣ୍ଡ ସେବାଗୁଡ଼ିକ ଆରମ୍ଭ କରିବାକୁ ଏକ ସହଯୋଗୀ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index f9dac96d7aee..82ad6c071446 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"ਹੋਰ ਡੀਵਾਈਸਾਂ \'ਤੇ ਸੰਬੰਧੀ ਸੁਨੇਹੇ ਡਿਲੀਵਰ ਕਰਨ ਲਈ ਸੰਬੰਧੀ ਐਪ ਨੂੰ ਆਗਿਆ ਮਿਲਦੀ ਹੈ।"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"ਬੈਕਗ੍ਰਾਊਂਡ ਤੋਂ ਫੋਰਗ੍ਰਾਊਂਡ ਸੇਵਾਵਾਂ ਚਾਲੂ ਕਰੋ"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ਸੰਬੰਧੀ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਤੋਂ ਫੋਰਗ੍ਰਾਊਂਡ ਸੇਵਾਵਾਂ ਸ਼ੁਰੂ ਕਰਨ ਦੀ ਆਗਿਆ ਮਿਲਦੀ ਹੈ।"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index c57890538c3a..5b160ed290b3 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -2354,4 +2354,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Zezwala aplikacji towarzyszącej na wysyłanie wiadomości towarzyszących na inne urządzenia."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Uruchamianie usług na pierwszym planie podczas działania w tle"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Zezwala aplikacji towarzyszącej na uruchamianie usług działających na pierwszym planie, podczas gdy sama działa w tle."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 45d019e4d6c3..a6a281f60517 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Permite que um app complementar envie mensagens complementares para outros dispositivos."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Iniciar serviços em primeiro plano estando em segundo plano"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permite que um app complementar em segundo plano inicie serviços em primeiro plano."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 57285255e5d9..dda4f966538d 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Permite que uma app associada entregue mensagens associadas noutros dispositivos."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Iniciar em segundo plano serviços em primeiro plano"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permite que uma app associada em segundo plano inicie serviços em primeiro plano."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 45d019e4d6c3..a6a281f60517 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Permite que um app complementar envie mensagens complementares para outros dispositivos."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Iniciar serviços em primeiro plano estando em segundo plano"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permite que um app complementar em segundo plano inicie serviços em primeiro plano."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 5774809cb989..02e4a83b0fc2 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Permite unei aplicații partenere să trimită mesaje dispozitivelor însoțitoare."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Să inițieze servicii în prim-plan din fundal"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permite unei aplicații partenere să inițieze servicii în prim-plan din fundal."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index b0ff050e92b8..e098107f8bdb 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -2354,4 +2354,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Сопутствующее приложение сможет доставлять сообщения на другие устройства."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Запуск активных служб из фонового режима"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Сопутствующее приложение сможет запускать активные службы из фонового режима."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 81e6b55a1323..ff02130b5287 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"වෙනත් උපාංග වෙත සහායක පණිවිඩ බෙදා හැරීමට සහායක යෙදුමකට ඉඩ දෙයි."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"පසුබිමේ සිට පෙරබිම් සේවා ආරම්භ කරන්න"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"පසුබිමේ සිට පෙරබිම් සේවා ආරම්භ කිරීමට සහායක යෙදුමකට ඉඩ දෙයි."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index a40d19ae574b..57722867ce3a 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -2354,4 +2354,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Umožňuje sprievodnej aplikácii doručovať sprievodné správy do iných zariadení."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Spúšťať služby na popredí z pozadia"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Umožňuje sprievodnej aplikácii spúšťať služby na popredí z pozadia."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index d254e3f26e36..9aab2b615e64 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -2354,4 +2354,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Spremljevalni aplikaciji dovoljuje dostavljanje sporočil v druge naprave."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Zaganjanje storitev v ospredju iz ozadja"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Spremljevalni aplikaciji dovoljuje, da storitve v ospredju zažene iz ozadja."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index f0fb083f1861..c6775761c88b 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Lejon një aplikacion shoqërues të dërgojë mesazhe shoqëruese te pajisjet e tjera."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Fillo shërbimet në plan të parë nga sfondi"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Lejon një aplikacion shoqërues të fillojë shërbimet në plan të parë nga sfondi."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 71ead55fbca4..562ffec6a1a1 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -2353,4 +2353,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Дозвољава пратећој апликацији да шаље пратеће поруке на друге уређаје."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Покретање услуга у првом плану из позадине"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Дозвољава пратећој апликацији да покрене услуге у првом плану из позадине."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index a1dc313d1deb..f77f8e914d85 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Tillåter en tillhörande app att leverera meddelanden till andra enheter."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Starta förgrundstjänster i bakgrunden"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Tillåter att en tillhörande app startar förgrundstjänster i bakgrunden."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 1f3dd858b875..2b90303ebd50 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -2366,4 +2366,8 @@
<skip />
<!-- no translation found for permdesc_startForegroundServicesFromBackground (4071826571656001537) -->
<skip />
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index b71fb1926fed..fb137471e857 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"பிற சாதனங்களுக்குத் துணை மெசேஜ்களை வழங்க துணைத் தயாரிப்பு ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"பின்னணியில் இருந்து முன்புலச் சேவைகளைத் தொடங்குதல்"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"பின்னணியிலிருந்து முன்புலச் சேவைகளைத் தொடங்க துணைத் தயாரிப்பு ஆப்ஸை அனுமதிக்கும்."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 19a396427729..54718ba3462c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1190,7 +1190,7 @@
<string name="delete" msgid="1514113991712129054">"తొలగించండి"</string>
<string name="copyUrl" msgid="6229645005987260230">"URLని కాపీ చేయి"</string>
<string name="selectTextMode" msgid="3225108910999318778">"వచనాన్ని ఎంచుకోండి"</string>
- <string name="undo" msgid="3175318090002654673">"చర్య రద్దు చేయి"</string>
+ <string name="undo" msgid="3175318090002654673">"చర్య రద్దు చేయండి"</string>
<string name="redo" msgid="7231448494008532233">"చర్యను రిపీట్‌ చేయి"</string>
<string name="autofill" msgid="511224882647795296">"ఆటోఫిల్"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"వచన ఎంపిక"</string>
@@ -1749,7 +1749,7 @@
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"యాక్సెసిబిలిటీ బటన్‌తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"వాల్యూమ్ కీ షార్ట్‌కట్‌తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ఆఫ్ చేయబడింది"</string>
- <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"షార్ట్‌కట్‌లను ఎడిట్ చేయి"</string>
+ <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"షార్ట్‌కట్‌లను ఎడిట్ చేయండి"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"పూర్తయింది"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"షార్ట్‌కట్‌ను ఆఫ్ చేయి"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"షార్ట్‌కట్‌ను ఉపయోగించు"</string>
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"ఇతర పరికరాలకు సహాయక మెసేజ్‌లను డెలివరీ చేయడానికి సహాయక యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"ఫోర్‌గ్రౌండ్ సర్వీస్‌లను లను బ్యాక్‌గ్రౌండ్ నుండి ప్రారంభించండి"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"బ్యాక్‌గ్రౌండ్ నుండి ఫోర్‌గ్రౌండ్ సర్వీస్‌లను ప్రారంభించడానికి సహాయక యాప్‌ను అనుమతిస్తుంది."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 80743b18f030..ff59ce147714 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"อนุญาตให้แอปที่ใช้ร่วมกันส่งข้อความที่ใช้ร่วมกันไปยังอุปกรณ์อื่นๆ"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"เริ่มการทำงานของบริการที่ทำงานอยู่เบื้องหน้าโดยให้อนุญาตจากเบื้องหลัง"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"อนุญาตจากเบื้องหลังให้แอปที่ใช้ร่วมกันเริ่มการทำงานของบริการที่ทำงานอยู่เบื้องหน้า"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index dfe2357ba736..257fdd09e3af 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Nagbibigay-daan sa kasamang app na maghatid ng mga mensahe ng kasamang app sa iba pang device."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Magsimula ng mga serbisyo sa foreground mula sa background"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Nagbibigay-daan sa kasamang app na magsimula ng mga serbisyo sa foreground mula sa background."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b53fb60ca312..22bb9987bb9d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Tamamlayıcı uygulamanın diğer cihazlara tamamlayıcı mesajlar iletmesine izin verir."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Arka plandan ön plan hizmetleri başlatma"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Tamamlayıcı uygulamanın arka plandan ön plan hizmetlerini başlatmasına izin verir."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index ea8efd1f422e..ed035daf1c9a 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -2354,4 +2354,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Дозволяє супутньому додатку відображати власні повідомлення на інших пристроях."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Запускати активні сервіси у фоновому режимі"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Дозволяє супутньому додатку запускати активні сервіси у фоновому режимі."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 11f5f68a0151..936a547d855e 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"ساتھی ایپ کو دوسرے آلات پر ساتھی پیغامات ڈیلیور کرنے کی اجازت دیتی ہے۔"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"پس منظر سے پیش منظر کی سروسز شروع کریں"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ساتھی ایپ کو پس منظر سے پیش منظر کی سروسز شروع کرنے کی اجازت دیتی ہے۔"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 83394bb322bf..71ab3e453e0d 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Hamroh ilovaga boshqa qurilmalarga hamroh xabarlarni yetkazib berishga ruxsat beradi."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Faol xizmatlarni fonda ishga tushirish"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Hamroh ilovaga faol xizmatlarni fonda ishga tushirishga ruxsat beradi."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 6a1438f6c834..3ead6299f7e2 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Cho phép một ứng dụng đồng hành gửi thông báo đồng hành đến các thiết bị khác."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Bắt đầu các dịch vụ trên nền trước từ nền"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Cho phép một ứng dụng đồng hành bắt đầu các dịch vụ trên nền trước từ nền."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ba49875500d9..551f5e645ca4 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"允许配套应用向其他设备发送配套消息。"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"从后台启动前台服务"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"允许配套应用从后台启动前台服务。"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 1a89ea8fe93b..2b3480df98c5 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"允許隨附應用程式傳送隨附訊息至其他裝置。"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"從背景啟動前景服務"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"允許隨附應用程式從背景啟動前景服務。"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 1dfcd67b3e4a..e8e13823bca4 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"允許隨附應用程式將自身產生的訊息傳送給其他裝置。"</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"從背景啟動前景服務"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"允許隨附應用程式從背景啟動前景服務。"</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index e257c3acacde..d8306be25597 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -2352,4 +2352,8 @@
<string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Ivumela i-app ehambisanayo ukuletha imilayezo ehambisanayo kwamanye amadivayisi."</string>
<string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Qala amasevisi angaphambili kusukela ngemuva"</string>
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Ivumela i-app ehambisanayo ukuthi iqale amasevisi angaphambili kusukela ngemuva."</string>
+ <!-- no translation found for mic_access_on_toast (2666925317663845156) -->
+ <skip />
+ <!-- no translation found for mic_access_off_toast (8111040892954242437) -->
+ <skip />
</resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index d40adf53abd1..925b5d394e5a 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -140,4 +140,10 @@
only single logical modem, by using its data connection in addition to cellular IMS. -->
<bool name="config_enable_virtual_dsda">false</bool>
<java-symbol type="bool" name="config_enable_virtual_dsda" />
+
+ <!-- Whether to enable getSubscriptionUserHandle() api.
+ If the value is true, return user handle associated with the subscription.
+ If the value is set to false, return null. -->
+ <bool name="config_enable_get_subscription_user_handle">true</bool>
+ <java-symbol type="bool" name="config_enable_get_subscription_user_handle" />
</resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 4ddbf58757b8..e71277dc0414 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -279,6 +279,9 @@
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_TEXT_SUGGESTIONS}. -->
<item type="id" name="accessibilityActionShowTextSuggestions" />
+ <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SCROLL_IN_DIRECTION}. -->
+ <item type="id" name="accessibilityActionScrollInDirection" />
+
<!-- View tag for remote views to store the index of the next child when adding nested remote views dynamically. -->
<item type="id" name="remote_views_next_child" />
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 0aeee10caddf..1bc1ca666e98 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -125,6 +125,7 @@
<public name="bold" />
<public name="italic" />
<public name="underline" />
+ <public name="accessibilityActionScrollInDirection" />
</staging-public-group>
<staging-public-group type="style" first-id="0x01cc0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index add7307bbda4..d4aa47c0da1f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2822,230 +2822,9 @@
<!-- Message in a javascript dialog asking if the user wishes to leave the current page -->
<string name="js_dialog_before_unload"><xliff:g id="message">%s</xliff:g>\n\nAre you sure you want to navigate away from this page?</string>
- <!-- Title of the WebView save password dialog. If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. -->
- <string name="save_password_label">Confirm</string>
-
- <!-- Toast for double-tap -->
- <string name="double_tap_toast">Tip: Double-tap to zoom in and out.</string>
-
- <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form, and the user has configured an AutoFill profile [CHAR-LIMIT=8] -->
- <string name="autofill_this_form">Autofill</string>
- <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=19] -->
- <string name="setup_autofill">Set up Autofill</string>
-
<!-- Title of fullscreen autofill window, including the name of which autofill service it is using [CHAR-LIMIT=NONE] -->
<string name="autofill_window_title">Autofill with <xliff:g id="serviceName" example="MyPass">%1$s</xliff:g></string>
- <!-- String used to separate FirstName and LastName when writing out a local name
- e.g. John<separator>Smith [CHAR-LIMIT=NONE]-->
- <string name="autofill_address_name_separator">\u0020</string>
- <!-- Format string for displaying a name. $1 is First Name, $2 is autofill_address_name_separator, $3 is Last Name.
- e.g. (John)( )(Smith) -->
- <string name="autofill_address_summary_name_format">$1$2$3</string>
-
- <!-- String used to separate Name and Address Line 1
- e.g. John Smith<separator>123 Main Street [CHAR-LIMIT=NONE]-->
- <string name="autofill_address_summary_separator">,\u0020</string>
- <!-- Format string for displaying a name and address summary. $1 is the Full Name, $2 is autofill_address_summary_separator, $3 is the Address
- e.g. (John Smith)(, )(123 Main Street) -->
- <string name="autofill_address_summary_format">$1$2$3</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_attention_ignored_re">attention|attn</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_region_ignored_re">province|region|other<!-- es -->|provincia<!-- pt-BR, pt-PT -->|bairro|suburb</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_company_re">company|business|organization|organisation|department<!-- de-DE -->|firma|firmenname<!-- es -->|empresa<!-- fr-FR -->|societe|société<!-- it-IT -->|ragione.?sociale<!-- ja-JP -->|会社<!-- ru -->|название.?компании<!-- zh-CN -->|单位|公司</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_address_line_1_re">address.?line|address1|addr1|street<!-- de-DE -->|strasse|straße|hausnummer|housenumber<!-- en-GB -->|house.?name<!-- es -->|direccion|dirección<!-- fr-FR -->|adresse<!-- it-IT -->|indirizzo<!-- ja-JP -->|住所1<!-- pt-BR, pt-PT -->|morada|endereço<!-- ru -->|Адрес<!-- zh-CN -->|地址</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_address_line_1_label_re">address<!-- fr-FR -->|adresse<!-- it-IT -->|indirizzo<!-- ja-JP -->|住所<!-- zh-CN -->|地址</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_address_line_2_re">address.?line2|address2|addr2|street|suite|unit<!-- de-DE -->|adresszusatz|ergänzende.?angaben<!-- es -->|direccion2|colonia|adicional<!-- fr-FR -->|addresssuppl|complementnom|appartement<!-- it-IT -->|indirizzo2<!-- ja-JP -->|住所2</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_address_line_3_re">address.?line3|address3|addr3|street|line3<!-- es -->|municipio<!-- fr-FR -->|batiment|residence<!-- it-IT -->|indirizzo3</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_country_re">country|location<!-- ja-JP -->|国<!-- zh-CN -->|国家</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_zip_code_re">zip|postal|post code|pcode|^1z$<!-- de-DE -->|postleitzahl<!-- es -->|cp<!-- fr-FR -->|cdp<!-- it-IT -->|cap<!-- ja-JP -->|郵便番号<!-- pt-BR, pt-PT -->|codigo|codpos|cep<!-- ru -->|Почтовый.?Индекс<!--zh-CN -->|邮政编码|邮编<!-- zh-TW -->|郵遞區號</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_zip_4_re">zip|^-$|post2<!-- pt-BR, pt-PT -->|codpos2</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_city_re">city|town<!-- de-DE -->|ort|stadt<!-- en-AU -->|suburb<!-- es -->|ciudad|provincia|localidad|poblacion<!-- fr-FR -->|ville|commune<!-- it-IT -->|localita<!-- ja-JP -->|市区町村<!-- pt-BR, pt-PT -->|cidade<!-- ru -->|Город<!-- zh-CN -->|市<!-- zh-TW -->|分區</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_state_re">state|county|region|province<!-- de-DE -->|land<!-- en-UK -->|county|principality<!-- ja-JP -->|都道府県<!-- pt-BR, pt-PT -->|estado|provincia<!-- ru -->|область<!-- zh-CN -->|省<!-- zh-TW -->|地區</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_address_type_same_as_re">same as</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_address_type_use_my_re">use my</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_billing_designator_re">bill</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_shipping_designator_re">ship</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_email_re">e.?mail<!-- ja-JP -->|メールアドレス<!-- ru -->|Электронной.?Почты<!-- zh-CN -->|邮件|邮箱<!-- zh-TW -->|電郵地址</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_username_re">user.?name|user.?id<!-- de-DE -->|vollständiger.?name<!-- zh-CN -->|用户名</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_name_re">^name|full.?name|your.?name|customer.?name|firstandlastname<!-- es -->|nombre.*y.*apellidos<!-- fr-FR -->|^nom<!-- ja-JP -->|お名前|氏名<!-- pt-BR, pt-PT -->|^nome<!-- zh-CN -->|姓名</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_name_specific_re">^name<!-- fr-FR -->|^nom<!-- pt-BR, pt-PT -->|^nome</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
-
- <string name="autofill_first_name_re">irst.*name|initials|fname|first$<!-- de-DE -->|vorname<!-- es -->|nombre<!-- fr-FR -->|forename|prénom|prenom<!-- ja-JP -->|名<!-- pt-BR, pt-PT -->|nome<!-- ru -->|Имя</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_middle_initial_re">middle.*initial|m\\.i\\.|mi$</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_middle_name_re">middle.*name|mname|middle$<!-- es -->|apellido.?materno|lastlastname</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_last_name_re">last.*name|lname|surname|last$<!-- de-DE -->|nachname<!-- es -->|apellidos<!-- fr-FR -->|famille|^nom<!-- it-IT -->|cognome<!-- ja-JP -->|姓<!-- pt-BR, pt-PT -->|morada|apelidos|surename|sobrenome<!-- ru -->|Фамилия</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_re">phone<!-- de-DE -->|telefonnummer<!-- es -->|telefono|teléfono<!-- fr-FR -->|telfixe<!-- ja-JP -->|電話<!-- pt-BR, pt-PT -->|telefone|telemovel<!-- ru -->|телефон<!-- zh-CN -->|电话</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_area_code_re">area.*code|acode|area</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_prefix_re">prefix<!-- fr-FR -->|preselection<!-- pt-BR, pt-PT -->|ddd</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_suffix_re">suffix</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_extension_re">ext<!-- pt-BR, pt-PT -->|ramal</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_name_on_card_re">card.?holder|name.?on.?card|ccname|owner<!-- de-DE -->|karteninhaber<!-- es -->|nombre.*tarjeta<!-- fr-FR -->|nom.*carte<!-- it-IT -->|nome.*cart<!-- ja-JP -->|名前<!-- ru -->|Имя.*карты<!-- zh-CN -->|信用卡开户名|开户名|持卡人姓名<!-- zh-TW -->|持卡人姓名</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_name_on_card_contextual_re">name</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_card_cvc_re">verification|card identification|cvn|security code|cvv code|cvc</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_card_number_re">number|card.?#|card.?no|ccnum<!-- de-DE -->|nummer<!-- es -->|credito|numero|número<!-- fr-FR -->|numéro<!-- ja-JP -->|カード番号<!-- ru -->|Номер.*карты<!-- zh-CN -->|信用卡号|信用卡号码<!-- zh-TW -->|信用卡卡號</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_expiration_month_re">expir|exp.*month|exp.*date|ccmonth<!-- de-DE -->|gueltig|gültig|monat<!-- es -->|fecha<!-- fr-FR -->|date.*exp<!-- it-IT -->|scadenza<!-- ja-JP -->|有効期限<!-- pt-BR, pt-PT -->|validade<!-- ru -->|Срок действия карты<!-- zh-CN -->|月</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_expiration_date_re">exp|^/|year<!-- de-DE -->|ablaufdatum|gueltig|gültig|yahr<!-- es -->|fecha<!-- it-IT -->|scadenza<!-- ja-JP -->|有効期限<!-- pt-BR, pt-PT -->|validade<!-- ru -->|Срок действия карты<!-- zh-CN -->|年|有效期</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_card_ignored_re">^card</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_fax_re">fax<!-- fr-FR -->|télécopie|telecopie<!-- ja-JP -->|ファックス<!-- ru -->|факс<!-- zh-CN -->|传真<!-- zh-TW -->|傳真</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_country_code_re">country.*code|ccode|_cc</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_area_code_notext_re">^\\($</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_prefix_separator_re">^-$|^\\)$</string>
-
- <!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_suffix_separator_re">^-$</string>
-
- <!-- Label in a web form for "Province" [CHAR-LIMIT=NONE] -->
- <string name="autofill_province">Province</string>
-
- <!-- Label in a web form for "Postal code" [CHAR-LIMIT=NONE] -->
- <string name="autofill_postal_code">Postal code</string>
-
- <!-- Label in a web form for "State" [CHAR-LIMIT=NONE] -->
- <string name="autofill_state">State</string>
-
- <!-- Label in a web form for "ZIP code" [CHAR-LIMIT=NONE] -->
- <string name="autofill_zip_code">ZIP code</string>
-
- <!-- Label in a web form for "County" [CHAR-LIMIT=NONE] -->
- <string name="autofill_county">County</string>
-
- <!-- Label in a web form for "Island" [CHAR-LIMIT=NONE] -->
- <string name="autofill_island">Island</string>
-
- <!-- Label in a web form for "District" [CHAR-LIMIT=NONE] -->
- <string name="autofill_district">District</string>
-
- <!-- Label in a web form for "Department" [CHAR-LIMIT=NONE] -->
- <string name="autofill_department">Department</string>
-
- <!-- Label in a web form for "Prefecture" [CHAR-LIMIT=NONE] -->
- <string name="autofill_prefecture">Prefecture</string>
-
- <!-- Label in a web form for "Parish" [CHAR-LIMIT=NONE] -->
- <string name="autofill_parish">Parish</string>
-
- <!-- Label in a web form for "Area" [CHAR-LIMIT=NONE] -->
- <string name="autofill_area">Area</string>
-
- <!-- Label in a web form for "Emirate" [CHAR-LIMIT=NONE] -->
- <string name="autofill_emirate">Emirate</string>
-
-
- <!-- Title of an application permission, listed so the user can choose whether
- they want to allow the application to do this. -->
- <string name="permlab_readHistoryBookmarks">read your Web bookmarks and history</string>
- <!-- Description of an application permission, listed so the user can choose whether
- they want to allow the application to do this. -->
- <string name="permdesc_readHistoryBookmarks">Allows the app to read the
- history of all URLs that the Browser has visited, and all of the Browser\'s
- bookmarks. Note: this permission may not be enforced by third-party
- browsers or other applications with web browsing capabilities.</string>
- <!-- Title of an application permission, listed so the user can choose whether
- they want to allow the application to do this. -->
-
- <string name="permlab_writeHistoryBookmarks">write web bookmarks and history</string>
- <!-- Description of an application permission, listed so the user can choose whether
- they want to allow the application to do this. -->
- <string name="permdesc_writeHistoryBookmarks" product="tablet">Allows the
- app to modify the Browser\'s history or bookmarks stored on your tablet.
- This may allow the app to erase or modify Browser data. Note: this
- permission may note be enforced by third-party browsers or other
- applications with web browsing capabilities.</string>
- <!-- Description of an application permission, listed so the user can choose whether
- they want to allow the application to do this. -->
- <string name="permdesc_writeHistoryBookmarks" product="tv">Allows the
- app to modify the Browser\'s history or bookmarks stored on your Android TV device.
- This may allow the app to erase or modify Browser data. Note: this
- permission may note be enforced by third-party browsers or other
- applications with web browsing capabilities.</string>
- <!-- Description of an application permission, listed so the user can choose whether
- they want to allow the application to do this. -->
- <string name="permdesc_writeHistoryBookmarks" product="default">Allows the
- app to modify the Browser\'s history or bookmarks stored on your phone.
- This may allow the app to erase or modify Browser data. Note:
- this permission may note be enforced by third-party browsers or other
- applications with web browsing capabilities.</string>
-
<!-- Title of an application permission, listed so the user can choose whether
they want to allow the application to do this. -->
<string name="permlab_setAlarm">set an alarm</string>
@@ -3063,45 +2842,9 @@
<string name="permdesc_addVoicemail">Allows the app to add messages
to your voicemail inbox.</string>
- <!-- Title of an application permission, listed so the user can choose whether
- they want to allow the application to do this. -->
- <string name="permlab_writeGeolocationPermissions">modify Browser geolocation permissions</string>
- <!-- Description of an application permission, listed so the user can choose whether
- they want to allow the application to do this. -->
- <string name="permdesc_writeGeolocationPermissions">Allows the app to modify the
- Browser\'s geolocation permissions. Malicious apps
- may use this to allow sending location information to arbitrary web sites.</string>
-
- <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Text in the save password dialog, asking if the browser should remember a password. -->
- <string name="save_password_message">Do you want the browser to remember this password?</string>
- <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Button in the save password dialog, saying not to remember this password. -->
- <string name="save_password_notnow">Not now</string>
- <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Button in the save password dialog, saying to remember this password. -->
- <string name="save_password_remember">Remember</string>
- <!-- Button in the save password dialog, saying never to remember this password. This should be short. Should be "Never for this site". But it is too long, use "Never" instead -->
- <string name="save_password_never">Never</string>
-
- <!-- Displayed to the user when they do not have permission to open a particular web page. -->
- <string name="open_permission_deny">You don\'t have permission to open this page.</string>
-
- <!-- Displayed to the user to confirm that they have copied text from a web page to the clipboard. -->
- <string name="text_copied">Text copied to clipboard.</string>
-
- <!-- Displayed to the user to inform them that an app has accessed clipboard data (pasted as in "copy and paste") that was copied from another app [CHAR LIMIT=50] -->
- <string name="pasted_from_app"><xliff:g id="pasting_app_name" example="Gmail">%1$s</xliff:g> pasted from <xliff:g id="source_app_name" example="Chrome">%2$s</xliff:g></string>
-
<!-- Displayed to the user to inform them that an app has accessed clipboard data (pasted as in "copy and paste") [CHAR LIMIT=50] -->
<string name="pasted_from_clipboard"><xliff:g id="pasting_app_name" example="Gmail">%1$s</xliff:g> pasted from your clipboard</string>
- <!-- Displayed to the user to inform them that an app has accessed text from clipboard (pasted as in "copy and paste") [CHAR LIMIT=50] -->
- <string name="pasted_text"><xliff:g id="pasting_app_name" example="Gmail">%1$s</xliff:g> pasted text you copied</string>
-
- <!-- Displayed to the user to inform them that an app has accessed an image from clipboard (pasted as in "copy and paste") [CHAR LIMIT=50] -->
- <string name="pasted_image"><xliff:g id="pasting_app_name" example="Gmail">%1$s</xliff:g> pasted an image you copied</string>
-
- <!-- Displayed to the user to inform them that an app has accessed content from clipboard (pasted as in "copy and paste") [CHAR LIMIT=50] -->
- <string name="pasted_content"><xliff:g id="pasting_app_name" example="Gmail">%1$s</xliff:g> pasted content you copied</string>
-
<!-- Menu item displayed at the end of a menu to allow users to see another page worth of menu items. This is shown on any app's menu as long as the app has too many items in the menu.-->
<string name="more_item_label">More</string>
<!-- Prepended to the shortcut for a menu item to indicate that the user should hold the MENU button together with the shortcut to invoke the item. For example, if the shortcut to open a new tab in browser is MENU and B together, then this would be prepended to the letter "B" -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 14c751f58967..6d393f697e20 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -571,11 +571,7 @@
<java-symbol type="string" name="prepend_shortcut_label" />
<java-symbol type="string" name="private_dns_broken_detailed" />
<java-symbol type="string" name="paste_as_plain_text" />
- <java-symbol type="string" name="pasted_from_app" />
<java-symbol type="string" name="pasted_from_clipboard" />
- <java-symbol type="string" name="pasted_text" />
- <java-symbol type="string" name="pasted_image" />
- <java-symbol type="string" name="pasted_content" />
<java-symbol type="string" name="replace" />
<java-symbol type="string" name="undo" />
<java-symbol type="string" name="redo" />
@@ -624,65 +620,7 @@
<java-symbol type="string" name="activitychooserview_choose_application_error" />
<java-symbol type="string" name="alternate_eri_file" />
<java-symbol type="string" name="alwaysUse" />
- <java-symbol type="string" name="autofill_address_line_1_label_re" />
- <java-symbol type="string" name="autofill_address_line_1_re" />
- <java-symbol type="string" name="autofill_address_line_2_re" />
- <java-symbol type="string" name="autofill_address_line_3_re" />
- <java-symbol type="string" name="autofill_address_name_separator" />
- <java-symbol type="string" name="autofill_address_summary_format" />
- <java-symbol type="string" name="autofill_address_summary_name_format" />
- <java-symbol type="string" name="autofill_address_summary_separator" />
- <java-symbol type="string" name="autofill_address_type_same_as_re" />
- <java-symbol type="string" name="autofill_address_type_use_my_re" />
- <java-symbol type="string" name="autofill_area" />
- <java-symbol type="string" name="autofill_area_code_notext_re" />
- <java-symbol type="string" name="autofill_area_code_re" />
- <java-symbol type="string" name="autofill_attention_ignored_re" />
- <java-symbol type="string" name="autofill_billing_designator_re" />
- <java-symbol type="string" name="autofill_card_cvc_re" />
- <java-symbol type="string" name="autofill_card_ignored_re" />
- <java-symbol type="string" name="autofill_card_number_re" />
- <java-symbol type="string" name="autofill_city_re" />
- <java-symbol type="string" name="autofill_company_re" />
- <java-symbol type="string" name="autofill_country_code_re" />
- <java-symbol type="string" name="autofill_country_re" />
- <java-symbol type="string" name="autofill_county" />
- <java-symbol type="string" name="autofill_department" />
- <java-symbol type="string" name="autofill_district" />
- <java-symbol type="string" name="autofill_email_re" />
- <java-symbol type="string" name="autofill_emirate" />
- <java-symbol type="string" name="autofill_expiration_date_re" />
- <java-symbol type="string" name="autofill_expiration_month_re" />
- <java-symbol type="string" name="autofill_fax_re" />
- <java-symbol type="string" name="autofill_first_name_re" />
- <java-symbol type="string" name="autofill_island" />
- <java-symbol type="string" name="autofill_last_name_re" />
- <java-symbol type="string" name="autofill_middle_initial_re" />
- <java-symbol type="string" name="autofill_middle_name_re" />
- <java-symbol type="string" name="autofill_name_on_card_contextual_re" />
- <java-symbol type="string" name="autofill_name_on_card_re" />
- <java-symbol type="string" name="autofill_name_re" />
- <java-symbol type="string" name="autofill_name_specific_re" />
- <java-symbol type="string" name="autofill_parish" />
- <java-symbol type="string" name="autofill_phone_extension_re" />
- <java-symbol type="string" name="autofill_phone_prefix_re" />
- <java-symbol type="string" name="autofill_phone_prefix_separator_re" />
- <java-symbol type="string" name="autofill_phone_re" />
- <java-symbol type="string" name="autofill_phone_suffix_re" />
- <java-symbol type="string" name="autofill_phone_suffix_separator_re" />
- <java-symbol type="string" name="autofill_postal_code" />
- <java-symbol type="string" name="autofill_prefecture" />
- <java-symbol type="string" name="autofill_province" />
- <java-symbol type="string" name="autofill_region_ignored_re" />
- <java-symbol type="string" name="autofill_shipping_designator_re" />
- <java-symbol type="string" name="autofill_state" />
- <java-symbol type="string" name="autofill_state_re" />
- <java-symbol type="string" name="autofill_this_form" />
- <java-symbol type="string" name="autofill_username_re" />
<java-symbol type="string" name="autofill_window_title" />
- <java-symbol type="string" name="autofill_zip_4_re" />
- <java-symbol type="string" name="autofill_zip_code" />
- <java-symbol type="string" name="autofill_zip_code_re" />
<java-symbol type="string" name="badPin" />
<java-symbol type="string" name="badPuk" />
<java-symbol type="string" name="byteShort" />
@@ -742,7 +680,6 @@
<java-symbol type="string" name="display_manager_overlay_display_name" />
<java-symbol type="string" name="display_manager_overlay_display_secure_suffix" />
<java-symbol type="string" name="display_manager_overlay_display_title" />
- <java-symbol type="string" name="double_tap_toast" />
<java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" />
<java-symbol type="string" name="elapsed_time_short_format_mm_ss" />
<java-symbol type="string" name="emailTypeCustom" />
@@ -861,7 +798,6 @@
<java-symbol type="string" name="number_picker_increment_scroll_mode" />
<java-symbol type="string" name="old_app_action" />
<java-symbol type="string" name="older" />
- <java-symbol type="string" name="open_permission_deny" />
<java-symbol type="string" name="orgTypeCustom" />
<java-symbol type="string" name="orgTypeOther" />
<java-symbol type="string" name="orgTypeWork" />
@@ -986,11 +922,6 @@
<java-symbol type="string" name="roamingText8" />
<java-symbol type="string" name="roamingText9" />
<java-symbol type="string" name="roamingTextSearching" />
- <java-symbol type="string" name="save_password_label" />
- <java-symbol type="string" name="save_password_message" />
- <java-symbol type="string" name="save_password_never" />
- <java-symbol type="string" name="save_password_notnow" />
- <java-symbol type="string" name="save_password_remember" />
<java-symbol type="string" name="selected" />
<java-symbol type="string" name="sendText" />
<java-symbol type="string" name="sending" />
@@ -1044,7 +975,6 @@
<java-symbol type="string" name="sync_really_delete" />
<java-symbol type="string" name="sync_too_many_deletes_desc" />
<java-symbol type="string" name="sync_undo_deletes" />
- <java-symbol type="string" name="text_copied" />
<java-symbol type="string" name="time_of_day" />
<java-symbol type="string" name="time_picker_decrement_hour_button" />
<java-symbol type="string" name="time_picker_decrement_minute_button" />
diff --git a/core/tests/coretests/src/android/text/EmojiConsistencyTest.java b/core/tests/coretests/src/android/text/EmojiConsistencyTest.java
new file mode 100644
index 000000000000..c6e9e9cce829
--- /dev/null
+++ b/core/tests/coretests/src/android/text/EmojiConsistencyTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import static junit.framework.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiConsistencyTest {
+
+ @Test
+ public void exclusionList_isEmpty() {
+ assertEquals(EmojiConsistency.getEmojiConsistencySet(), Collections.emptySet());
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
index e384e699333b..d1f0b5e1dd30 100644
--- a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
@@ -23,36 +23,53 @@ 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 android.provider.DeviceConfig;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.google.common.truth.Expect;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.time.Duration;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class LatencyTrackerTest {
+ private static final String TAG = LatencyTrackerTest.class.getSimpleName();
private static final String ENUM_NAME_PREFIX = "UIACTION_LATENCY_REPORTED__ACTION__";
+ private static final String ACTION_ENABLE_SUFFIX = "_enable";
+ private static final Duration TEST_TIMEOUT = Duration.ofMillis(500);
@Rule
public final Expect mExpect = Expect.create();
+ @Before
+ public void setUp() {
+ DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ LatencyTracker.SETTINGS_ENABLED_KEY);
+ getAllActions().forEach(action -> {
+ DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ action.getName().toLowerCase() + ACTION_ENABLE_SUFFIX);
+ });
+ }
+
@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());
-
+ List<Field> actions = getAllActions();
Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
.filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
&& Modifier.isStatic(f.getModifiers())
@@ -84,13 +101,7 @@ public class LatencyTrackerTest {
@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());
-
+ List<Field> cujEnumFields = getAllActions();
HashSet<Integer> allValues = new HashSet<>();
for (Field field : cujEnumFields) {
int fieldValue = field.getInt(null);
@@ -106,6 +117,95 @@ public class LatencyTrackerTest {
}
}
+ @Test
+ public void testIsEnabled_globalEnabled() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ LatencyTracker.SETTINGS_ENABLED_KEY, "true", false);
+ LatencyTracker latencyTracker = new LatencyTracker();
+ waitForLatencyTrackerToUpdateProperties(latencyTracker);
+ assertThat(latencyTracker.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void testIsEnabled_globalDisabled() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
+ LatencyTracker latencyTracker = new LatencyTracker();
+ waitForLatencyTrackerToUpdateProperties(latencyTracker);
+ assertThat(latencyTracker.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void testIsEnabledAction_useGlobalValueWhenActionEnableIsNotSet() {
+ LatencyTracker latencyTracker = new LatencyTracker();
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ Log.i(TAG, "setting property=" + LatencyTracker.SETTINGS_ENABLED_KEY + ", value=true");
+ latencyTracker.mDeviceConfigPropertiesUpdated.close();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ LatencyTracker.SETTINGS_ENABLED_KEY, "true", false);
+ waitForLatencyTrackerToUpdateProperties(latencyTracker);
+ assertThat(
+ latencyTracker.isEnabled(action)).isTrue();
+
+ Log.i(TAG, "setting property=" + LatencyTracker.SETTINGS_ENABLED_KEY
+ + ", value=false");
+ latencyTracker.mDeviceConfigPropertiesUpdated.close();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
+ waitForLatencyTrackerToUpdateProperties(latencyTracker);
+ assertThat(latencyTracker.isEnabled(action)).isFalse();
+ }
+
+ @Test
+ public void testIsEnabledAction_actionPropertyOverridesGlobalProperty()
+ throws DeviceConfig.BadConfigException {
+ LatencyTracker latencyTracker = new LatencyTracker();
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ String actionEnableProperty = "action_show_voice_interaction" + ACTION_ENABLE_SUFFIX;
+ Log.i(TAG, "setting property=" + actionEnableProperty + ", value=true");
+
+ latencyTracker.mDeviceConfigPropertiesUpdated.close();
+ Map<String, String> properties = new HashMap<String, String>() {{
+ put(LatencyTracker.SETTINGS_ENABLED_KEY, "false");
+ put(actionEnableProperty, "true");
+ }};
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ properties));
+ waitForLatencyTrackerToUpdateProperties(latencyTracker);
+ assertThat(latencyTracker.isEnabled(action)).isTrue();
+
+ latencyTracker.mDeviceConfigPropertiesUpdated.close();
+ Log.i(TAG, "setting property=" + actionEnableProperty + ", value=false");
+ properties.put(LatencyTracker.SETTINGS_ENABLED_KEY, "true");
+ properties.put(actionEnableProperty, "false");
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ properties));
+ waitForLatencyTrackerToUpdateProperties(latencyTracker);
+ assertThat(latencyTracker.isEnabled(action)).isFalse();
+ }
+
+ private void waitForLatencyTrackerToUpdateProperties(LatencyTracker latencyTracker) {
+ try {
+ Thread.sleep(TEST_TIMEOUT.toMillis());
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ assertThat(latencyTracker.mDeviceConfigPropertiesUpdated.block(
+ TEST_TIMEOUT.toMillis())).isTrue();
+ }
+
+ private List<Field> getAllActions() {
+ return Arrays.stream(LatencyTracker.class.getDeclaredFields())
+ .filter(field -> field.getName().startsWith("ACTION_")
+ && Modifier.isStatic(field.getModifiers())
+ && field.getType() == int.class)
+ .collect(Collectors.toList());
+ }
+
private int getIntFieldChecked(Field field) {
try {
return field.getInt(null);
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 1ab5e4bef2bb..278b9580fa2e 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -3907,12 +3907,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/TaskDisplayArea.java"
},
- "1648338379": {
- "message": "Display id=%d is ignoring all orientation requests, return %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"1653025361": {
"message": "Register task fragment organizer=%s uid=%d pid=%d",
"level": "VERBOSE",
@@ -4141,6 +4135,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.java"
},
+ "1877863087": {
+ "message": "Display id=%d is ignoring orientation request for %d, return %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"1878927091": {
"message": "prepareSurface: No changes in animation for %s",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index cc2bb63ca8e1..0bdf98c8680f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -155,6 +155,7 @@ public final class DeviceStateManagerFoldingFeatureProducer
* Adds the data to the storeFeaturesConsumer when the data is ready.
* @param storeFeaturesConsumer a consumer to collect the data when it is first available.
*/
+ @Override
public void getData(Consumer<List<CommonFoldingFeature>> storeFeaturesConsumer) {
mRawFoldSupplier.getData((String displayFeaturesString) -> {
if (TextUtils.isEmpty(displayFeaturesString)) {
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 5400164d9aae..ce7d695beb2a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -108,6 +108,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Currently applied split configuration.
@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.
@@ -125,6 +126,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
@Nullable
private SplitAttributesCalculator mSplitAttributesCalculator;
+
/**
* Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
* below it.
@@ -230,6 +232,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@NonNull
+ @GuardedBy("mLock")
+ @VisibleForTesting
List<EmbeddingRule> getSplitRules() {
return mSplitRules;
}
@@ -246,7 +250,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
/**
- * Clears the listener set in {@link SplitController#setSplitInfoListener}.
+ * Clears the listener set in {@link SplitController#setSplitInfoCallback(Consumer)}.
*/
@Override
public void clearSplitInfoCallback() {
@@ -472,6 +476,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
updateContainersInTask(wct, taskContainer);
}
+ @GuardedBy("mLock")
void updateContainersInTaskIfVisible(@NonNull WindowContainerTransaction wct, int taskId) {
final TaskContainer taskContainer = getTaskContainer(taskId);
if (taskContainer != null && taskContainer.isVisible()) {
@@ -479,6 +484,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
+ @GuardedBy("mLock")
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
@@ -763,6 +769,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Places the given activity to the top most TaskFragment in the task if there is any.
*/
+ @GuardedBy("mLock")
@VisibleForTesting
void placeActivityInTopContainer(@NonNull WindowContainerTransaction wct,
@NonNull Activity activity) {
@@ -886,6 +893,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/** Finds the activity below the given activity. */
@VisibleForTesting
@Nullable
+ @GuardedBy("mLock")
Activity findActivityBelow(@NonNull Activity activity) {
Activity activityBelow = null;
final TaskFragmentContainer container = getContainerWithActivity(activity);
@@ -1221,6 +1229,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return null;
}
+ @GuardedBy("mLock")
TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity, int taskId) {
return newContainer(pendingAppearedActivity, pendingAppearedActivity, taskId);
}
@@ -1358,7 +1367,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Removes a secondary container for the given primary container if an existing split is
* already registered.
*/
- void removeExistingSecondaryContainers(@NonNull WindowContainerTransaction wct,
+ // Suppress GuardedBy warning because lint asks to mark this method as
+ // @GuardedBy(existingSplitContainer.getSecondaryContainer().mController.mLock), which is mLock
+ // itself
+ @SuppressWarnings("GuardedBy")
+ @GuardedBy("mLock")
+ private void removeExistingSecondaryContainers(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer primaryContainer) {
// If the primary container was already in a split - remove the secondary container that
// is now covered by the new one that replaced it.
@@ -1376,6 +1390,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Returns the topmost not finished container in Task of given task id.
*/
+ @GuardedBy("mLock")
@Nullable
TaskFragmentContainer getTopActiveContainer(int taskId) {
final TaskContainer taskContainer = mTaskContainers.get(taskId);
@@ -1747,6 +1762,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Nullable
+ @GuardedBy("mLock")
TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
@@ -1760,6 +1776,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Nullable
+ @GuardedBy("mLock")
TaskContainer getTaskContainer(int taskId) {
return mTaskContainers.get(taskId);
}
@@ -1768,6 +1785,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return mHandler;
}
+ @GuardedBy("mLock")
int getTaskId(@NonNull Activity activity) {
// Prefer to get the taskId from TaskFragmentContainer because Activity.getTaskId() is an
// IPC call.
@@ -1860,6 +1878,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* @see #shouldRetainAssociatedContainer(TaskFragmentContainer, TaskFragmentContainer)
*/
+ @GuardedBy("mLock")
boolean shouldRetainAssociatedActivity(@NonNull TaskFragmentContainer finishingContainer,
@NonNull Activity associatedActivity) {
final TaskFragmentContainer associatedContainer = getContainerWithActivity(
@@ -1980,6 +1999,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@VisibleForTesting
class ActivityStartMonitor extends Instrumentation.ActivityMonitor {
@VisibleForTesting
+ @GuardedBy("mLock")
Intent mCurrentIntent;
@Override
@@ -2044,18 +2064,21 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Override
public void onStartActivityResult(int result, @NonNull Bundle bOptions) {
super.onStartActivityResult(result, bOptions);
- if (mCurrentIntent != null && result != START_SUCCESS) {
- // Clear the pending appeared intent if the activity was not started successfully.
- final IBinder token = bOptions.getBinder(
- ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN);
- if (token != null) {
- final TaskFragmentContainer container = getContainer(token);
- if (container != null) {
- container.clearPendingAppearedIntentIfNeeded(mCurrentIntent);
+ synchronized (mLock) {
+ if (mCurrentIntent != null && result != START_SUCCESS) {
+ // Clear the pending appeared intent if the activity was not started
+ // successfully.
+ final IBinder token = bOptions.getBinder(
+ ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN);
+ if (token != null) {
+ final TaskFragmentContainer container = getContainer(token);
+ if (container != null) {
+ container.clearPendingAppearedIntentIfNeeded(mCurrentIntent);
+ }
}
}
+ mCurrentIntent = null;
}
- mCurrentIntent = null;
}
}
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 a432e2b10c14..9db9f8788190 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -39,7 +39,6 @@ import android.view.WindowMetrics;
import android.window.TaskFragmentCreationParams;
import android.window.WindowContainerTransaction;
-import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -63,7 +62,10 @@ import java.util.concurrent.Executor;
/**
* Controls the visual presentation of the splits according to the containers formed by
* {@link SplitController}.
+ *
+ * Note that all calls into this class must hold the {@link SplitController} internal lock.
*/
+@SuppressWarnings("GuardedBy")
class SplitPresenter extends JetpackTaskFragmentOrganizer {
@VisibleForTesting
static final int POSITION_START = 0;
@@ -163,7 +165,6 @@ 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) {
@@ -210,7 +211,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* created and the activity will be re-parented to it.
* @param rule The split rule to be applied to the container.
*/
- @GuardedBy("mController.mLock")
void createNewSplitContainer(@NonNull WindowContainerTransaction wct,
@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity,
@NonNull SplitPairRule rule) {
@@ -285,7 +285,6 @@ 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,
@@ -328,7 +327,6 @@ 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) {
@@ -369,7 +367,6 @@ 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,
@@ -393,7 +390,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* creation has not been reported from the server yet.
*/
// TODO(b/190433398): Handle resize if the fragment hasn't appeared yet.
- void resizeTaskFragmentIfRegistered(@NonNull WindowContainerTransaction wct,
+ private void resizeTaskFragmentIfRegistered(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container,
@Nullable Rect bounds) {
if (container.getInfo() == null) {
@@ -520,7 +517,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
return !(splitAttributes.getSplitType() instanceof ExpandContainersSplitType);
}
- @GuardedBy("mController.mLock")
@NonNull
SplitAttributes computeSplitAttributes(@NonNull TaskProperties taskProperties,
@NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
@@ -572,8 +568,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
}
@NonNull
- static Pair<Size, Size> getActivitiesMinDimensionsPair(@NonNull Activity primaryActivity,
- @NonNull Activity secondaryActivity) {
+ private static Pair<Size, Size> getActivitiesMinDimensionsPair(
+ @NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) {
return new Pair<>(getMinDimensions(primaryActivity), getMinDimensions(secondaryActivity));
}
@@ -619,7 +615,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
return new Size(windowLayout.minWidth, windowLayout.minHeight);
}
- static boolean boundsSmallerThanMinDimensions(@NonNull Rect bounds,
+ private static boolean boundsSmallerThanMinDimensions(@NonNull Rect bounds,
@Nullable Size minDimensions) {
if (minDimensions == null) {
return false;
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 8240874ca53d..6bfdfe7593b8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -253,6 +253,7 @@ class TaskFragmentContainer {
mPendingAppearedActivities.remove(activityToken);
}
+ @GuardedBy("mController.mLock")
void clearPendingAppearedActivities() {
final List<IBinder> cleanupActivities = new ArrayList<>(mPendingAppearedActivities);
// Clear mPendingAppearedActivities so that #getContainerWithActivity won't return the
@@ -452,6 +453,7 @@ class TaskFragmentContainer {
* Removes all activities that belong to this process and finishes other containers/activities
* configured to finish together.
*/
+ @GuardedBy("mController.mLock")
void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
if (!mIsFinished) {
@@ -476,6 +478,7 @@ class TaskFragmentContainer {
mInfo = null;
}
+ @GuardedBy("mController.mLock")
private void finishActivities(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
// Finish own activities
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 b70b320eee3c..84b2bfc38559 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -35,8 +35,8 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.window.WindowProvider;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiContext;
@@ -64,13 +64,19 @@ import java.util.function.Consumer;
public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private static final String TAG = "SampleExtension";
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private final Map<Context, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
new ArrayMap<>();
+ @GuardedBy("mLock")
private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
+ @GuardedBy("mLock")
private final List<CommonFoldingFeature> mLastReportedFoldingFeatures = new ArrayList<>();
+ @GuardedBy("mLock")
private final Map<IBinder, ConfigurationChangeListener> mConfigurationChangeListeners =
new ArrayMap<>();
@@ -85,7 +91,9 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
/** Registers to listen to {@link CommonFoldingFeature} changes */
public void addFoldingStateChangedCallback(Consumer<List<CommonFoldingFeature>> consumer) {
- mFoldingFeatureProducer.addDataChangedCallback(consumer);
+ synchronized (mLock) {
+ mFoldingFeatureProducer.addDataChangedCallback(consumer);
+ }
}
/**
@@ -114,28 +122,32 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
@Override
public void addWindowLayoutInfoListener(@NonNull @UiContext Context context,
@NonNull Consumer<WindowLayoutInfo> consumer) {
- if (mWindowLayoutChangeListeners.containsKey(context)
- // In theory this method can be called on the same consumer with different context.
- || mWindowLayoutChangeListeners.containsValue(consumer)) {
- return;
- }
- if (!context.isUiContext()) {
- throw new IllegalArgumentException("Context must be a UI Context, which should be"
- + " an Activity, WindowContext or InputMethodService");
- }
- mFoldingFeatureProducer.getData((features) -> {
- WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, features);
- consumer.accept(newWindowLayout);
- });
- mWindowLayoutChangeListeners.put(context, consumer);
-
- // TODO(b/258065175) Further extend this to ContextWrappers.
- if (context instanceof WindowProvider) {
+ synchronized (mLock) {
+ if (mWindowLayoutChangeListeners.containsKey(context)
+ // In theory this method can be called on the same consumer with different
+ // context.
+ || mWindowLayoutChangeListeners.containsValue(consumer)) {
+ return;
+ }
+ if (!context.isUiContext()) {
+ throw new IllegalArgumentException("Context must be a UI Context, which should be"
+ + " an Activity, WindowContext or InputMethodService");
+ }
+ mFoldingFeatureProducer.getData((features) -> {
+ WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, features);
+ consumer.accept(newWindowLayout);
+ });
+ mWindowLayoutChangeListeners.put(context, consumer);
+
final IBinder windowContextToken = context.getWindowContextToken();
- final ConfigurationChangeListener listener =
- new ConfigurationChangeListener(windowContextToken);
- context.registerComponentCallbacks(listener);
- mConfigurationChangeListeners.put(windowContextToken, listener);
+ if (windowContextToken != null) {
+ // We register component callbacks for window contexts. For activity contexts, they
+ // will receive callbacks from NotifyOnConfigurationChanged instead.
+ final ConfigurationChangeListener listener =
+ new ConfigurationChangeListener(windowContextToken);
+ context.registerComponentCallbacks(listener);
+ mConfigurationChangeListeners.put(windowContextToken, listener);
+ }
}
}
@@ -146,25 +158,29 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
*/
@Override
public void removeWindowLayoutInfoListener(@NonNull Consumer<WindowLayoutInfo> consumer) {
- for (Context context : mWindowLayoutChangeListeners.keySet()) {
- if (!mWindowLayoutChangeListeners.get(context).equals(consumer)) {
- continue;
- }
- if (context instanceof WindowProvider) {
+ synchronized (mLock) {
+ for (Context context : mWindowLayoutChangeListeners.keySet()) {
+ if (!mWindowLayoutChangeListeners.get(context).equals(consumer)) {
+ continue;
+ }
final IBinder token = context.getWindowContextToken();
- context.unregisterComponentCallbacks(mConfigurationChangeListeners.get(token));
- mConfigurationChangeListeners.remove(token);
+ if (token != null) {
+ context.unregisterComponentCallbacks(mConfigurationChangeListeners.get(token));
+ mConfigurationChangeListeners.remove(token);
+ }
+ break;
}
- break;
+ mWindowLayoutChangeListeners.values().remove(consumer);
}
- mWindowLayoutChangeListeners.values().remove(consumer);
}
+ @GuardedBy("mLock")
@NonNull
- Set<Context> getContextsListeningForLayoutChanges() {
+ private Set<Context> getContextsListeningForLayoutChanges() {
return mWindowLayoutChangeListeners.keySet();
}
+ @GuardedBy("mLock")
private boolean isListeningForLayoutChanges(IBinder token) {
for (Context context: getContextsListeningForLayoutChanges()) {
if (token.equals(Context.getToken(context))) {
@@ -174,10 +190,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
return false;
}
- protected boolean hasListeners() {
- return !mWindowLayoutChangeListeners.isEmpty();
- }
-
/**
* A convenience method to translate from the common feature state to the extensions feature
* state. More specifically, translates from {@link CommonFoldingFeature.State} to
@@ -201,13 +213,17 @@ 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);
- WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, storedFeatures);
- layoutConsumer.accept(newWindowLayout);
+ synchronized (mLock) {
+ 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);
+ WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, storedFeatures);
+ layoutConsumer.accept(newWindowLayout);
+ }
}
}
@@ -232,7 +248,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
@NonNull
public WindowLayoutInfo getCurrentWindowLayoutInfo(int displayId,
@NonNull WindowConfiguration windowConfiguration) {
- return getWindowLayoutInfo(displayId, windowConfiguration, mLastReportedFoldingFeatures);
+ synchronized (mLock) {
+ return getWindowLayoutInfo(displayId, windowConfiguration,
+ mLastReportedFoldingFeatures);
+ }
}
/** @see #getWindowLayoutInfo(Context, List) */
@@ -308,9 +327,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
return false;
}
final int windowingMode;
- if (context instanceof Activity) {
+ IBinder activityToken = context.getActivityToken();
+ if (activityToken != null) {
final Configuration taskConfig = ActivityClient.getInstance().getTaskConfiguration(
- context.getActivityToken());
+ activityToken);
if (taskConfig == null) {
// If we cannot determine the task configuration for any reason, it is likely that
// we won't be able to determine its position correctly as well. DisplayFeatures'
@@ -329,6 +349,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
return !WindowConfiguration.inMultiWindowMode(windowingMode);
}
+ @GuardedBy("mLock")
private void onDisplayFeaturesChangedIfListening(@NonNull IBinder token) {
if (isListeningForLayoutChanges(token)) {
mFoldingFeatureProducer.getData(
@@ -340,13 +361,17 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
super.onActivityCreated(activity, savedInstanceState);
- onDisplayFeaturesChangedIfListening(activity.getActivityToken());
+ synchronized (mLock) {
+ onDisplayFeaturesChangedIfListening(activity.getActivityToken());
+ }
}
@Override
public void onActivityConfigurationChanged(Activity activity) {
super.onActivityConfigurationChanged(activity);
- onDisplayFeaturesChangedIfListening(activity.getActivityToken());
+ synchronized (mLock) {
+ onDisplayFeaturesChangedIfListening(activity.getActivityToken());
+ }
}
}
@@ -359,7 +384,9 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
- onDisplayFeaturesChangedIfListening(mToken);
+ synchronized (mLock) {
+ onDisplayFeaturesChangedIfListening(mToken);
+ }
}
@Override
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 dcb03aa0b365..d051ca350682 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -17,81 +17,81 @@
<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">"Zatvori"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"Proširi"</string>
- <string name="pip_phone_settings" msgid="5468987116750491918">"Podešavanja"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Uđi na podeljeni ekran"</string>
- <string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
- <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meni slike u slici."</string>
- <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je slika u slici"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"Ako ne želite da <xliff:g id="NAME">%s</xliff:g> koristi ovu funkciju, dodirnite da biste otvorili podešavanja i isključili je."</string>
- <string name="pip_play" msgid="3496151081459417097">"Pusti"</string>
- <string name="pip_pause" msgid="690688849510295232">"Pauziraj"</string>
- <string name="pip_skip_to_next" msgid="8403429188794867653">"Pređi na sledeće"</string>
- <string name="pip_skip_to_prev" msgid="7172158111196394092">"Pređi na prethodno"</string>
- <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promenite veličinu"</string>
- <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavite u tajnu memoriju"</string>
- <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Uklonite iz tajne memorije"</string>
- <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi sa podeljenim ekranom."</string>
- <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podeljeni ekran."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova aplikacija može da se otvori samo u jednom prozoru."</string>
- <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
- <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
- <string name="accessibility_divider" msgid="703810061635792791">"Razdelnik podeljenog ekrana"</string>
- <string name="divider_title" msgid="5482989479865361192">"Razdelnik podeljenog ekrana"</string>
- <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Režim celog ekrana za levi ekran"</string>
- <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Levi ekran 70%"</string>
- <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Levi ekran 50%"</string>
- <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Levi ekran 30%"</string>
- <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Režim celog ekrana za donji ekran"</string>
- <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Režim celog ekrana za gornji ekran"</string>
- <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gornji ekran 70%"</string>
- <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gornji ekran 50%"</string>
- <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gornji ekran 30%"</string>
- <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Režim celog ekrana za donji ekran"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Korišćenje režima jednom rukom"</string>
- <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Da biste izašli, prevucite nagore od dna ekrana ili dodirnite bilo gde iznad aplikacije"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Pokrenite režim jednom rukom"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Izađite iz režima jednom rukom"</string>
- <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Podešavanja za <xliff:g id="APP_NAME">%1$s</xliff:g> oblačiće"</string>
- <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Preklapanje"</string>
- <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodaj ponovo u grupu"</string>
- <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <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> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
- <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Premesti gore levo"</string>
- <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premesti gore desno"</string>
- <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premesti dole levo"</string>
- <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premesti dole desno"</string>
- <string name="bubbles_app_settings" msgid="3617224938701566416">"Podešavanja za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
- <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne koristi oblačiće za konverzaciju"</string>
- <string name="bubbles_user_education_title" msgid="2112319053732691899">"Ćaskajte u oblačićima"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nove konverzacije se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da biste otvorili oblačić. Prevucite da biste ga premestili."</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrolišite oblačiće u bilo kom trenutku"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dodirnite Upravljajte da biste isključili oblačiće iz ove aplikacije"</string>
- <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Važi"</string>
- <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovde se prikazuju nedavni i odbačeni oblačići"</string>
- <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
- <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza."</string>
- <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>
- <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>
- <string name="minimize_button_text" msgid="271592547935841753">"Umanjite"</string>
- <string name="close_button_text" msgid="2913281996024033299">"Zatvorite"</string>
- <string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
- <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
- <string name="fullscreen_text" msgid="1162316685217676079">"Preko celog ekrana"</string>
- <string name="desktop_text" msgid="1077633567027630454">"Režim za računare"</string>
- <string name="split_screen_text" msgid="1396336058129570886">"Podeljeni ekran"</string>
- <string name="more_button_text" msgid="3655388105592893530">"Još"</string>
- <string name="float_button_text" msgid="9221657008391364581">"Plutajuće"</string>
+ <string name="pip_phone_close" msgid="5783752637260411309">"Затвори"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Прошири"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Подешавања"</string>
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Уђи на подељени екран"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Мени"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Мени слике у слици."</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> је слика у слици"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ако не желите да <xliff:g id="NAME">%s</xliff:g> користи ову функцију, додирните да бисте отворили подешавања и искључили је."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Пусти"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Паузирај"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Пређи на следеће"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Пређи на претходно"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промените величину"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ставите у тајну меморију"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Уклоните из тајне меморије"</string>
+ <string name="dock_forced_resizable" msgid="1749750436092293116">"Апликација можда неће радити са подељеним екраном."</string>
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликација не подржава подељени екран."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ова апликација може да се отвори само у једном прозору."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликација можда неће функционисати на секундарном екрану."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Апликација не подржава покретање на секундарним екранима."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Разделник подељеног екрана"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Разделник подељеног екрана"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Режим целог екрана за леви екран"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Леви екран 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Леви екран 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Леви екран 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Режим целог екрана за доњи екран"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Режим целог екрана за горњи екран"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Горњи екран 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Горњи екран 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Горњи екран 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Режим целог екрана за доњи екран"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Коришћење режима једном руком"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Да бисте изашли, превуците нагоре од дна екрана или додирните било где изнад апликације"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Покрените режим једном руком"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Изађите из режима једном руком"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Подешавања за <xliff:g id="APP_NAME">%1$s</xliff:g> облачиће"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Преклапање"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Додај поново у групу"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> из апликације <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> из апликације <xliff:g id="APP_NAME">%2$s</xliff:g> и још <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Премести горе лево"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести доле лево"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести доле десно"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Подешавања за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Одбаци облачић"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не користи облачиће за конверзацију"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Ћаскајте у облачићима"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Нове конверзације се приказују као плутајуће иконе или облачићи. Додирните да бисте отворили облачић. Превуците да бисте га преместили."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Контролишите облачиће у било ком тренутку"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Додирните Управљајте да бисте искључили облачиће из ове апликације"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Важи"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема недавних облачића"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Овде се приказују недавни и одбачени облачићи"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Додирните да бисте рестартовали ову апликацију ради бољег приказа."</string>
+ <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>
+ <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>
+ <string name="minimize_button_text" msgid="271592547935841753">"Умањите"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Затворите"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Идентификатор"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Преко целог екрана"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Режим за рачунаре"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Подељени екран"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Још"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Плутајуће"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
index 6dc4ab1cea79..34950027772b 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
@@ -17,18 +17,18 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Slika u slici"</string>
- <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
- <string name="pip_close" msgid="2955969519031223530">"Zatvori"</string>
- <string name="pip_fullscreen" msgid="7278047353591302554">"Ceo ekran"</string>
- <string name="pip_move" msgid="158770205886688553">"Premesti"</string>
- <string name="pip_expand" msgid="1051966011679297308">"Proširi"</string>
- <string name="pip_collapse" msgid="3903295106641385962">"Skupi"</string>
- <string name="pip_edu_text" msgid="7930546669915337998">"Dvaput pritisnite "<annotation icon="home_icon">" HOME "</annotation>" za kontrole"</string>
- <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meni Slika u slici."</string>
- <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pomerite nalevo"</string>
- <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pomerite nadesno"</string>
- <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Pomerite nagore"</string>
- <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Pomerite nadole"</string>
- <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gotovo"</string>
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Слика у слици"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програм без наслова)"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Затвори"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Цео екран"</string>
+ <string name="pip_move" msgid="158770205886688553">"Премести"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Прошири"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Скупи"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Двапут притисните "<annotation icon="home_icon">" HOME "</annotation>" за контроле"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Мени Слика у слици."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Померите налево"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Померите надесно"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Померите нагоре"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Померите надоле"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Готово"</string>
</resources>
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 1a8b9540cbd0..2ac1dc0c4838 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -69,6 +69,9 @@ android_test {
enabled: false,
},
+ platform_apis: true,
+ certificate: "platform",
+
aaptflags: [
"--extra-packages",
"com.android.wm.shell.tests",
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index f0a4bd0f00f0..b563627edad9 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -33,6 +33,7 @@
#include "SkMaskFilter.h"
#include "SkPath.h"
#include "SkPathEffect.h"
+#include "SkPathUtils.h"
#include "SkShader.h"
#include "SkBlendMode.h"
#include "unicode/uloc.h"
@@ -809,7 +810,7 @@ namespace PaintGlue {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
- return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
+ return skpathutils::FillPathWithPaint(*src, *obj, dst) ? JNI_TRUE : JNI_FALSE;
}
static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) {
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index edacef04b50a..94c9d94a7c26 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -17,6 +17,7 @@
#include "HintSessionWrapper.h"
#include <dlfcn.h>
+#include <private/performance_hint_private.h>
#include <utils/Log.h>
#include <vector>
diff --git a/media/java/android/media/DeniedByServerException.java b/media/java/android/media/DeniedByServerException.java
index 9c1633adca97..98903ec1439f 100644
--- a/media/java/android/media/DeniedByServerException.java
+++ b/media/java/android/media/DeniedByServerException.java
@@ -24,4 +24,11 @@ public final class DeniedByServerException extends MediaDrmException {
public DeniedByServerException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * @hide
+ */
+ public DeniedByServerException(String message, int vendorError, int oemError, int context) {
+ super(message, vendorError, oemError, context);
+ }
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index da2795f6dcfc..df9c539cae4d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1709,6 +1709,33 @@ final public class MediaCodec {
*/
public static final int BUFFER_FLAG_MUXER_DATA = 16;
+ /**
+ * This indicates that the buffer is decoded and updates the internal state of the decoder,
+ * but does not produce any output buffer.
+ *
+ * When a buffer has this flag set,
+ * {@link OnFrameRenderedListener#onFrameRendered(MediaCodec, long, long)} and
+ * {@link Callback#onOutputBufferAvailable(MediaCodec, int, BufferInfo)} will not be called for
+ * that given buffer.
+ *
+ * For example, when seeking to a certain frame, that frame may need to reference previous
+ * frames in order for it to produce output. The preceding frames can be marked with this flag
+ * so that they are only decoded and their data is used when decoding the latter frame that
+ * should be initially displayed post-seek.
+ * Another example would be trick play, trick play is when a video is fast-forwarded and only a
+ * subset of the frames is to be rendered on the screen. The frames not to be rendered can be
+ * marked with this flag for the same reason as the above one.
+ * Marking frames with this flag improves the overall performance of playing a video stream as
+ * fewer frames need to be passed back to the app.
+ *
+ * In {@link CodecCapabilities#FEATURE_TunneledPlayback}, buffers marked with this flag
+ * are not rendered on the output surface.
+ *
+ * A frame should not be marked with this flag and {@link #BUFFER_FLAG_END_OF_STREAM}
+ * simultaneously, doing so will produce a {@link InvalidBufferFlagsException}
+ */
+ public static final int BUFFER_FLAG_DECODE_ONLY = 32;
+
/** @hide */
@IntDef(
flag = true,
@@ -1719,6 +1746,7 @@ final public class MediaCodec {
BUFFER_FLAG_END_OF_STREAM,
BUFFER_FLAG_PARTIAL_FRAME,
BUFFER_FLAG_MUXER_DATA,
+ BUFFER_FLAG_DECODE_ONLY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface BufferFlag {}
@@ -1744,6 +1772,8 @@ final public class MediaCodec {
private static final int CB_OUTPUT_AVAILABLE = 2;
private static final int CB_ERROR = 3;
private static final int CB_OUTPUT_FORMAT_CHANGE = 4;
+ private static final String EOS_AND_DECODE_ONLY_ERROR_MESSAGE = "An input buffer cannot have "
+ + "both BUFFER_FLAG_END_OF_STREAM and BUFFER_FLAG_DECODE_ONLY flags";
private class EventHandler extends Handler {
private MediaCodec mCodec;
@@ -2104,6 +2134,16 @@ final public class MediaCodec {
}
/**
+ * Thrown when a buffer is marked with an invalid combination of flags
+ * (e.g. both {@link #BUFFER_FLAG_END_OF_STREAM} and {@link #BUFFER_FLAG_DECODE_ONLY})
+ */
+ public class InvalidBufferFlagsException extends RuntimeException {
+ InvalidBufferFlagsException(String message) {
+ super(message);
+ }
+ }
+
+ /**
* Configures a component.
*
* @param format The format of the input data (decoder) or the desired
@@ -2480,10 +2520,22 @@ final public class MediaCodec {
/**
* Thrown when a crypto error occurs while queueing a secure input buffer.
*/
- public final static class CryptoException extends RuntimeException {
+ public final static class CryptoException extends RuntimeException
+ implements MediaDrmThrowable {
public CryptoException(int errorCode, @Nullable String detailMessage) {
- super(detailMessage);
+ this(detailMessage, errorCode, 0, 0, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public CryptoException(String message, int errorCode, int vendorError, int oemError,
+ int errorContext) {
+ super(message);
mErrorCode = errorCode;
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
}
/**
@@ -2602,7 +2654,22 @@ final public class MediaCodec {
return mErrorCode;
}
- private int mErrorCode;
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
+ private final int mErrorCode, mVendorError, mOemError, mErrorContext;
}
/**
@@ -2666,6 +2733,10 @@ final public class MediaCodec {
int index,
int offset, int size, long presentationTimeUs, int flags)
throws CryptoException {
+ if ((flags & BUFFER_FLAG_DECODE_ONLY) != 0
+ && (flags & BUFFER_FLAG_END_OF_STREAM) != 0) {
+ throw new InvalidBufferFlagsException(EOS_AND_DECODE_ONLY_ERROR_MESSAGE);
+ }
synchronized(mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException("queueInputBuffer() "
@@ -2936,6 +3007,10 @@ final public class MediaCodec {
@NonNull CryptoInfo info,
long presentationTimeUs,
int flags) throws CryptoException {
+ if ((flags & BUFFER_FLAG_DECODE_ONLY) != 0
+ && (flags & BUFFER_FLAG_END_OF_STREAM) != 0) {
+ throw new InvalidBufferFlagsException(EOS_AND_DECODE_ONLY_ERROR_MESSAGE);
+ }
synchronized(mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException("queueSecureInputBuffer() "
diff --git a/media/java/android/media/MediaCryptoException.java b/media/java/android/media/MediaCryptoException.java
index 32ddf4733b55..2a472fbfdfc3 100644
--- a/media/java/android/media/MediaCryptoException.java
+++ b/media/java/android/media/MediaCryptoException.java
@@ -22,8 +22,35 @@ import android.annotation.Nullable;
* Exception thrown if MediaCrypto object could not be instantiated or
* if unable to perform an operation on the MediaCrypto object.
*/
-public final class MediaCryptoException extends Exception {
+public final class MediaCryptoException extends Exception implements MediaDrmThrowable {
public MediaCryptoException(@Nullable String detailMessage) {
- super(detailMessage);
+ this(detailMessage, 0, 0, 0);
}
+
+ /**
+ * @hide
+ */
+ public MediaCryptoException(String message, int vendorError, int oemError, int errorContext) {
+ super(message);
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
+ }
+
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
+ private final int mVendorError, mOemError, mErrorContext;
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 762dea1393a0..0b086816ca4b 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -664,21 +664,33 @@ public final class MediaDrm implements AutoCloseable {
* strategy and details about each possible return value from {@link
* MediaDrmStateException#getErrorCode()}.
*/
- public static final class MediaDrmStateException extends java.lang.IllegalStateException {
- private final int mErrorCode;
+ public static final class MediaDrmStateException extends java.lang.IllegalStateException
+ implements MediaDrmThrowable {
+ private final int mErrorCode, mVendorError, mOemError, mErrorContext;
private final String mDiagnosticInfo;
/**
* @hide
*/
public MediaDrmStateException(int errorCode, @Nullable String detailMessage) {
+ this(detailMessage, errorCode, 0, 0, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public MediaDrmStateException(String detailMessage, int errorCode,
+ int vendorError, int oemError, int errorContext) {
super(detailMessage);
mErrorCode = errorCode;
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
// TODO get this from DRM session
final String sign = errorCode < 0 ? "neg_" : "";
mDiagnosticInfo =
- "android.media.MediaDrm.error_" + sign + Math.abs(errorCode);
+ "android.media.MediaDrm.error_" + sign + Math.abs(errorCode);
}
@@ -696,6 +708,21 @@ public final class MediaDrm implements AutoCloseable {
return mErrorCode;
}
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
/**
* Returns true if the {@link MediaDrmStateException} is a transient
* issue, perhaps due to resource constraints, and that the operation
@@ -727,10 +754,22 @@ public final class MediaDrm implements AutoCloseable {
* {@link #isTransient()} to determine whether the app should retry the
* failing operation.
*/
- public static final class SessionException extends RuntimeException {
+ public static final class SessionException extends RuntimeException
+ implements MediaDrmThrowable {
public SessionException(int errorCode, @Nullable String detailMessage) {
+ this(detailMessage, errorCode, 0, 0, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public SessionException(String detailMessage, int errorCode, int vendorError, int oemError,
+ int errorContext) {
super(detailMessage);
mErrorCode = errorCode;
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
}
/**
@@ -769,6 +808,21 @@ public final class MediaDrm implements AutoCloseable {
return mErrorCode;
}
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
/**
* Returns true if the {@link SessionException} is a transient
* issue, perhaps due to resource constraints, and that the operation
@@ -779,7 +833,7 @@ public final class MediaDrm implements AutoCloseable {
return mErrorCode == ERROR_RESOURCE_CONTENTION;
}
- private final int mErrorCode;
+ private final int mErrorCode, mVendorError, mOemError, mErrorContext;
}
/**
diff --git a/media/java/android/media/MediaDrmException.java b/media/java/android/media/MediaDrmException.java
index d547574e177b..58c5dd057731 100644
--- a/media/java/android/media/MediaDrmException.java
+++ b/media/java/android/media/MediaDrmException.java
@@ -19,8 +19,35 @@ package android.media;
/**
* Base class for MediaDrm exceptions
*/
-public class MediaDrmException extends Exception {
+public class MediaDrmException extends Exception implements MediaDrmThrowable {
public MediaDrmException(String detailMessage) {
- super(detailMessage);
+ this(detailMessage, 0, 0, 0);
}
+
+ /**
+ * @hide
+ */
+ public MediaDrmException(String message, int vendorError, int oemError, int errorContext) {
+ super(message);
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
+ }
+
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
+ private final int mVendorError, mOemError, mErrorContext;
}
diff --git a/media/java/android/media/MediaDrmResetException.java b/media/java/android/media/MediaDrmResetException.java
index 3b2da1e8bd25..ccd723bb614b 100644
--- a/media/java/android/media/MediaDrmResetException.java
+++ b/media/java/android/media/MediaDrmResetException.java
@@ -21,7 +21,7 @@ package android.media;
* due to a restart of the mediaserver process. To continue, the app must
* release the MediaDrm object, then create and initialize a new one.
*/
-public class MediaDrmResetException extends IllegalStateException {
+public class MediaDrmResetException extends IllegalStateException implements MediaDrmThrowable {
public MediaDrmResetException(String detailMessage) {
super(detailMessage);
}
diff --git a/media/java/android/media/MediaDrmThrowable.java b/media/java/android/media/MediaDrmThrowable.java
new file mode 100644
index 000000000000..38480d79c3b3
--- /dev/null
+++ b/media/java/android/media/MediaDrmThrowable.java
@@ -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.
+ */
+
+package android.media;
+
+/**
+ * A @{@link Throwable} thrown from {@link MediaDrm} or @{@link MediaCrypto} APIs
+ */
+public interface MediaDrmThrowable {
+ /**
+ * Returns {@link MediaDrm} plugin vendor defined error code associated with this {@link
+ * MediaDrmThrowable}.
+ * <p>
+ * Please consult the {@link MediaDrm} plugin vendor for details on the error code.
+ *
+ * @return an error code defined by the {@link MediaDrm} plugin vendor if available,
+ * otherwise 0.
+ */
+ public default int getVendorError() {
+ return 0;
+ }
+
+ /**
+ * Returns OEM or SOC specific error code associated with this {@link
+ * MediaDrmThrowable}.
+ * <p>
+ * Please consult the {@link MediaDrm} plugin, chip, or device vendor for details on the
+ * error code.
+ *
+ * @return an OEM or SOC specific error code if available, otherwise 0.
+ */
+ public default int getOemError() {
+ return 0;
+ }
+
+ /**
+ * Returns {@link MediaDrm} plugin vendor defined error context associated with this {@link
+ * MediaDrmThrowable}.
+ * <p>
+ * Please consult the {@link MediaDrm} plugin vendor for details on the error context.
+ *
+ * @return an opaque integer that would help the @{@link MediaDrm} vendor locate the
+ * source of the error if available, otherwise 0.
+ */
+ public default int getErrorContext() {
+ return 0;
+ }
+
+}
diff --git a/media/java/android/media/NotProvisionedException.java b/media/java/android/media/NotProvisionedException.java
index 32b8151a4d47..4b5a816c6642 100644
--- a/media/java/android/media/NotProvisionedException.java
+++ b/media/java/android/media/NotProvisionedException.java
@@ -26,4 +26,11 @@ public final class NotProvisionedException extends MediaDrmException {
public NotProvisionedException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * @hide
+ */
+ public NotProvisionedException(String message, int vendorError, int oemError, int context) {
+ super(message, vendorError, oemError, context);
+ }
}
diff --git a/media/java/android/media/ResourceBusyException.java b/media/java/android/media/ResourceBusyException.java
index a5abe2119e00..7aaf7ebc1142 100644
--- a/media/java/android/media/ResourceBusyException.java
+++ b/media/java/android/media/ResourceBusyException.java
@@ -24,4 +24,11 @@ public final class ResourceBusyException extends MediaDrmException {
public ResourceBusyException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * @hide
+ */
+ public ResourceBusyException(String message, int vendorError, int oemError, int context) {
+ super(message, vendorError, oemError, context);
+ }
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 9b0f0203f42b..4e6a0c540f49 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -330,7 +330,6 @@ LIBANDROID {
APerformanceHint_updateTargetWorkDuration; # introduced=Tiramisu
APerformanceHint_reportActualWorkDuration; # introduced=Tiramisu
APerformanceHint_closeSession; # introduced=Tiramisu
- APerformanceHint_sendHint; # introduced=UpsideDownCake
local:
*;
};
@@ -338,6 +337,7 @@ LIBANDROID {
LIBANDROID_PLATFORM {
global:
APerformanceHint_setIHintManagerForTesting;
+ APerformanceHint_sendHint;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
ASurfaceControl_unregisterSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 9e97bd33ce9c..43b3d2e94aac 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -285,14 +285,14 @@ int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session,
return session->reportActualWorkDuration(actualDurationNanos);
}
-int APerformanceHint_sendHint(APerformanceHintSession* session, int32_t hint) {
- return session->sendHint(hint);
-}
-
void APerformanceHint_closeSession(APerformanceHintSession* session) {
delete session;
}
+int APerformanceHint_sendHint(void* session, int32_t hint) {
+ return reinterpret_cast<APerformanceHintSession*>(session)->sendHint(hint);
+}
+
void APerformanceHint_setIHintManagerForTesting(void* iManager) {
delete gHintManagerForTesting;
gHintManagerForTesting = nullptr;
diff --git a/packages/BackupRestoreConfirmation/res/values-b+sr+Latn/strings.xml b/packages/BackupRestoreConfirmation/res/values-b+sr+Latn/strings.xml
index ab551201771c..e7bdd2f84bc8 100644
--- a/packages/BackupRestoreConfirmation/res/values-b+sr+Latn/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-b+sr+Latn/strings.xml
@@ -16,23 +16,23 @@
<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">"Rezervna kopije svih podataka"</string>
- <string name="restore_confirm_title" msgid="5469365809567486602">"Potpuno vraćanje"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"Zahtevana je potpuna rezervna kopija svih podataka na povezani računar. Da li želite da dozvolite to?\n\nAko niste lično zahtevali rezervnu kopiju, ne dozvoljavajte nastavak radnje."</string>
- <string name="allow_backup_button_label" msgid="4217228747769644068">"Napravi rezervnu kopiju mojih podataka"</string>
- <string name="deny_backup_button_label" msgid="6009119115581097708">"Ne pravi rezervne kopije"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"Zahtevano je potpuno vraćanje svih podataka sa povezanog računara. Da li želite da dozvolite to?\n\nAko niste lično zahtevali vraćanje, ne dozvoljavajte nastavak radnje. Time ćete zameniti sve podatke koji su trenutno na uređaju!"</string>
- <string name="allow_restore_button_label" msgid="3081286752277127827">"Vrati moje podatke"</string>
- <string name="deny_restore_button_label" msgid="1724367334453104378">"Ne vraćaj"</string>
- <string name="current_password_text" msgid="8268189555578298067">"Unesite trenutnu lozinku rezervne kopije u nastavku:"</string>
- <string name="device_encryption_restore_text" msgid="1570864916855208992">"Unesite lozinku uređaja za šifrovanje u nastavku."</string>
- <string name="device_encryption_backup_text" msgid="5866590762672844664">"Unesite lozinku uređaja za šifrovanje. Ovo će se koristiti i za šifrovanje rezervne arhive."</string>
- <string name="backup_enc_password_text" msgid="4981585714795233099">"Unesite lozinku koju ćete koristiti za šifrovanje podataka potpune rezervne kopije. Ako to polje ostavite prazno, koristiće se trenutna lozinka rezervne kopije:"</string>
- <string name="backup_enc_password_optional" msgid="1350137345907579306">"Ako želite da šifrujete podatke potpune rezervne kopije, unesite lozinku u nastavku."</string>
- <string name="restore_enc_password_text" msgid="6140898525580710823">"Ako su podaci za vraćanje šifrovani, unesite lozinku u nastavku:"</string>
- <string name="toast_backup_started" msgid="550354281452756121">"Pokretanje pravljenja rezervne kopije..."</string>
- <string name="toast_backup_ended" msgid="3818080769548726424">"Rezervna kopija je napravljena"</string>
- <string name="toast_restore_started" msgid="7881679218971277385">"Pokretanje vraćanja..."</string>
- <string name="toast_restore_ended" msgid="1764041639199696132">"Vraćanje je završeno"</string>
- <string name="toast_timeout" msgid="5276598587087626877">"Vreme za radnju je isteklo"</string>
+ <string name="backup_confirm_title" msgid="827563724209303345">"Резервна копије свих података"</string>
+ <string name="restore_confirm_title" msgid="5469365809567486602">"Потпуно враћање"</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"Захтевана је потпуна резервна копија свих података на повезани рачунар. Да ли желите да дозволите то?\n\nАко нисте лично захтевали резервну копију, не дозвољавајте наставак радње."</string>
+ <string name="allow_backup_button_label" msgid="4217228747769644068">"Направи резервну копију мојих података"</string>
+ <string name="deny_backup_button_label" msgid="6009119115581097708">"Не прави резервне копије"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"Захтевано је потпуно враћање свих података са повезаног рачунара. Да ли желите да дозволите то?\n\nАко нисте лично захтевали враћање, не дозвољавајте наставак радње. Тиме ћете заменити све податке који су тренутно на уређају!"</string>
+ <string name="allow_restore_button_label" msgid="3081286752277127827">"Врати моје податке"</string>
+ <string name="deny_restore_button_label" msgid="1724367334453104378">"Не враћај"</string>
+ <string name="current_password_text" msgid="8268189555578298067">"Унесите тренутну лозинку резервне копије у наставку:"</string>
+ <string name="device_encryption_restore_text" msgid="1570864916855208992">"Унесите лозинку уређаја за шифровање у наставку."</string>
+ <string name="device_encryption_backup_text" msgid="5866590762672844664">"Унесите лозинку уређаја за шифровање. Ово ће се користити и за шифровање резервне архиве."</string>
+ <string name="backup_enc_password_text" msgid="4981585714795233099">"Унесите лозинку коју ћете користити за шифровање података потпуне резервне копије. Ако то поље оставите празно, користиће се тренутна лозинка резервне копије:"</string>
+ <string name="backup_enc_password_optional" msgid="1350137345907579306">"Ако желите да шифрујете податке потпуне резервне копије, унесите лозинку у наставку."</string>
+ <string name="restore_enc_password_text" msgid="6140898525580710823">"Ако су подаци за враћање шифровани, унесите лозинку у наставку:"</string>
+ <string name="toast_backup_started" msgid="550354281452756121">"Покретање прављења резервне копије..."</string>
+ <string name="toast_backup_ended" msgid="3818080769548726424">"Резервна копија је направљена"</string>
+ <string name="toast_restore_started" msgid="7881679218971277385">"Покретање враћања..."</string>
+ <string name="toast_restore_ended" msgid="1764041639199696132">"Враћање је завршено"</string>
+ <string name="toast_timeout" msgid="5276598587087626877">"Време за радњу је истекло"</string>
</resources>
diff --git a/packages/CarrierDefaultApp/res/values-am/strings.xml b/packages/CarrierDefaultApp/res/values-am/strings.xml
index 0efdbc420592..ae87b17ad9ea 100644
--- a/packages/CarrierDefaultApp/res/values-am/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-am/strings.xml
@@ -14,16 +14,10 @@
<string name="ssl_error_warning" msgid="3127935140338254180">"ለመቀላቀል እየሞከሩ ያሉት አውታረ መረብ የደህንነት ችግሮች አሉበት።"</string>
<string name="ssl_error_example" msgid="6188711843183058764">"ለምሳሌ፣ የመግቢያ ገጹ የሚታየው ድርጅት ላይሆን ይችላል።"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"ለማንኛውም በአሳሽ በኩል ይቀጥሉ"</string>
- <!-- no translation found for network_boost_notification_channel (5430986172506159199) -->
- <skip />
- <!-- no translation found for network_boost_notification_title (8226368121348880044) -->
- <skip />
- <!-- no translation found for network_boost_notification_detail (3812434025544196192) -->
- <skip />
- <!-- no translation found for network_boost_notification_button_not_now (4129218252146702688) -->
- <skip />
- <!-- no translation found for network_boost_notification_button_manage (1511552684142641182) -->
- <skip />
- <!-- no translation found for slice_purchase_app_label (915654761797446390) -->
- <skip />
+ <string name="network_boost_notification_channel" msgid="5430986172506159199">"የአውታረ መረብ መጨመር"</string>
+ <string name="network_boost_notification_title" msgid="8226368121348880044">"%s ውሂብን መጨመር ይመክራል"</string>
+ <string name="network_boost_notification_detail" msgid="3812434025544196192">"ለተሻለ አፈጻጸም የአውታረ መረብ መጨመርን ይግዙ"</string>
+ <string name="network_boost_notification_button_not_now" msgid="4129218252146702688">"አሁን አይደለም"</string>
+ <string name="network_boost_notification_button_manage" msgid="1511552684142641182">"አስተዳድር"</string>
+ <string name="slice_purchase_app_label" msgid="915654761797446390">"የአውታረ መረብ መጨመርን ይግዙ"</string>
</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ar/strings.xml b/packages/CarrierDefaultApp/res/values-ar/strings.xml
index 1194e157de59..ed0a711dab01 100644
--- a/packages/CarrierDefaultApp/res/values-ar/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ar/strings.xml
@@ -8,9 +8,7 @@
<string name="portal_notification_detail" msgid="2295729385924660881">"‏النقر للانتقال إلى موقع %s الإلكتروني"</string>
<string name="no_data_notification_detail" msgid="3112125343857014825">"‏يُرجى الاتصال بمقدم الخدمة %s"</string>
<string name="no_mobile_data_connection_title" msgid="7449525772416200578">"لا يوجد اتصال بيانات الجوال"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for no_mobile_data_connection (544980465184147010) -->
- <skip />
+ <string name="no_mobile_data_connection" msgid="544980465184147010">"‏إضافة بيانات أو خطة تجوال خلال %s"</string>
<string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"حالة بيانات الجوّال"</string>
<string name="action_bar_label" msgid="4290345990334377177">"تسجيل الدخول إلى شبكة الجوّال"</string>
<string name="ssl_error_warning" msgid="3127935140338254180">"الشبكة التي تحاول الانضمام إليها بها مشاكل أمنية."</string>
diff --git a/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml b/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml
index a1974f0da1aa..84db181b1af5 100644
--- a/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml
@@ -2,22 +2,22 @@
<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="5247871339820894594">"CarrierDefaultApp"</string>
- <string name="android_system_label" msgid="2797790869522345065">"Mobilni operater"</string>
- <string name="portal_notification_id" msgid="5155057562457079297">"Mobilni podaci su potrošeni"</string>
- <string name="no_data_notification_id" msgid="668400731803969521">"Mobilni podaci su deaktivirani"</string>
- <string name="portal_notification_detail" msgid="2295729385924660881">"Dodirnite da biste posetili veb-sajt %s"</string>
- <string name="no_data_notification_detail" msgid="3112125343857014825">"Kontaktirajte dobavljača usluge %s"</string>
- <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Nema veze za prenos podataka preko mobilnog operatera"</string>
- <string name="no_mobile_data_connection" msgid="544980465184147010">"Dodajte podatke ili paket za roming preko operatera %s"</string>
- <string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Status mobilnih podataka"</string>
- <string name="action_bar_label" msgid="4290345990334377177">"Prijavite se na mobilnu mrežu"</string>
- <string name="ssl_error_warning" msgid="3127935140338254180">"Mreža kojoj pokušavate da se pridružite ima bezbednosnih problema."</string>
- <string name="ssl_error_example" msgid="6188711843183058764">"Na primer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string>
- <string name="ssl_error_continue" msgid="1138548463994095584">"Ipak nastavi preko pregledača"</string>
- <string name="network_boost_notification_channel" msgid="5430986172506159199">"Pojačanje mreže"</string>
- <string name="network_boost_notification_title" msgid="8226368121348880044">"%s preporučuje povećanje podataka"</string>
- <string name="network_boost_notification_detail" msgid="3812434025544196192">"Kupite pojačanje mreže za bolji učinak"</string>
- <string name="network_boost_notification_button_not_now" msgid="4129218252146702688">"Ne sada"</string>
- <string name="network_boost_notification_button_manage" msgid="1511552684142641182">"Upravljajte"</string>
- <string name="slice_purchase_app_label" msgid="915654761797446390">"Kupite pojačanje mreže."</string>
+ <string name="android_system_label" msgid="2797790869522345065">"Мобилни оператер"</string>
+ <string name="portal_notification_id" msgid="5155057562457079297">"Мобилни подаци су потрошени"</string>
+ <string name="no_data_notification_id" msgid="668400731803969521">"Мобилни подаци су деактивирани"</string>
+ <string name="portal_notification_detail" msgid="2295729385924660881">"Додирните да бисте посетили веб-сајт %s"</string>
+ <string name="no_data_notification_detail" msgid="3112125343857014825">"Контактирајте добављача услуге %s"</string>
+ <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Нема везе за пренос података преко мобилног оператера"</string>
+ <string name="no_mobile_data_connection" msgid="544980465184147010">"Додајте податке или пакет за роминг преко оператера %s"</string>
+ <string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Статус мобилних података"</string>
+ <string name="action_bar_label" msgid="4290345990334377177">"Пријавите се на мобилну мрежу"</string>
+ <string name="ssl_error_warning" msgid="3127935140338254180">"Мрежа којој покушавате да се придружите има безбедносних проблема."</string>
+ <string name="ssl_error_example" msgid="6188711843183058764">"На пример, страница за пријављивање можда не припада приказаној организацији."</string>
+ <string name="ssl_error_continue" msgid="1138548463994095584">"Ипак настави преко прегледача"</string>
+ <string name="network_boost_notification_channel" msgid="5430986172506159199">"Појачање мреже"</string>
+ <string name="network_boost_notification_title" msgid="8226368121348880044">"%s препоручује повећање података"</string>
+ <string name="network_boost_notification_detail" msgid="3812434025544196192">"Купите појачање мреже за бољи учинак"</string>
+ <string name="network_boost_notification_button_not_now" msgid="4129218252146702688">"Не сада"</string>
+ <string name="network_boost_notification_button_manage" msgid="1511552684142641182">"Управљајте"</string>
+ <string name="slice_purchase_app_label" msgid="915654761797446390">"Купите појачање мреже."</string>
</resources>
diff --git a/packages/CarrierDefaultApp/res/values-te/strings.xml b/packages/CarrierDefaultApp/res/values-te/strings.xml
index d1e49ca40b7c..a6113aed7604 100644
--- a/packages/CarrierDefaultApp/res/values-te/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-te/strings.xml
@@ -13,7 +13,7 @@
<string name="action_bar_label" msgid="4290345990334377177">"మొబైల్ నెట్‌వర్క్‌కి సైన్ ఇన్ చేయండి"</string>
<string name="ssl_error_warning" msgid="3127935140338254180">"మీరు చేరడానికి ప్రయత్నిస్తున్న నెట్‌వర్క్ భద్రతా సమస్యలను కలిగి ఉంది."</string>
<string name="ssl_error_example" msgid="6188711843183058764">"ఉదాహరణకు, లాగిన్ పేజీ చూపిన సంస్థకు చెందినది కాకపోవచ్చు."</string>
- <string name="ssl_error_continue" msgid="1138548463994095584">"ఏదేమైనా బ్రౌజర్ ద్వారా కొనసాగించు"</string>
+ <string name="ssl_error_continue" msgid="1138548463994095584">"ఏదేమైనా బ్రౌజర్ ద్వారా కొనసాగించండి"</string>
<string name="network_boost_notification_channel" msgid="5430986172506159199">"నెట్‌వర్క్ బూస్ట్"</string>
<string name="network_boost_notification_title" msgid="8226368121348880044">"డేటా బూస్ట్‌ను %s సిఫార్సు చేస్తోంది"</string>
<string name="network_boost_notification_detail" msgid="3812434025544196192">"మెరుగైన పనితీరు కోసం నెట్‌వర్క్ బూస్ట్‌ను కొనుగోలు చేయండి"</string>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index 50c2ccb8005e..26d2278062c1 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -16,63 +16,63 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4470785958457506021">"Menadžer pridruženog uređaja"</string>
- <string name="confirmation_title" msgid="3785000297483688997">"Dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa uređaju &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="summary_watch" msgid="4085794790142204006">"Aplikacija je potrebna za upravljanje uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> će dobiti dozvolu za interakciju sa obaveštenjima i pristup dozvolama za telefon, SMS, kontakte, kalendar, evidencije poziva i uređaje u blizini."</string>
- <string name="summary_watch_single_device" msgid="1523091550243476756">"Aplikacija je potrebna za upravljanje uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> će dobiti dozvolu za interakciju sa ovim dozvolama:"</string>
+ <string name="app_label" msgid="4470785958457506021">"Менаџер придруженог уређаја"</string>
+ <string name="confirmation_title" msgid="3785000297483688997">"Дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; приступа уређају &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
+ <string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" msgid="4085794790142204006">"Апликација је потребна за управљање уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> ће добити дозволу за интеракцију са обавештењима и приступ дозволама за телефон, SMS, контакте, календар, евиденције позива и уређаје у близини."</string>
+ <string name="summary_watch_single_device" msgid="1523091550243476756">"Апликација је потребна за управљање уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> ће добити дозволу за интеракцију са овим дозволама:"</string>
<!-- no translation found for profile_name_glasses (8488394059007275998) -->
<skip />
<!-- no translation found for summary_glasses (3195267006147355861) -->
<skip />
<!-- no translation found for summary_glasses_single_device (5725890636605211803) -->
<skip />
- <string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa ovim informacijama sa telefona"</string>
- <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluge na više uređaja"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za strimovanje aplikacija između uređaja"</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">"Dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa ovim informacijama sa telefona"</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="helper_title_computer" msgid="4671071173916176037">"Google Play usluge"</string>
- <string name="helper_summary_computer" msgid="9050724687678157852">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za pristup slikama, medijskom sadržaju i obaveštenjima sa telefona"</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>
<!-- no translation found for title_nearby_device_streaming (179278282547719200) -->
<skip />
<!-- no translation found for helper_title_nearby_device_streaming (6124438217620593669) -->
<skip />
<!-- no translation found for helper_summary_nearby_device_streaming (5538329403511524333) -->
<skip />
- <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
+ <string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
- <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
- <string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string>
- <string name="consent_back" msgid="2560683030046918882">"Nazad"</string>
- <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Aplikcijama na uređaju &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; dajte sve dozvole kao na uređaju &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;To može da obuhvata pristup mikrofonu, kameri i lokaciji, kao i drugim osetljivim dozvolama na uređaju &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;U svakom trenutku možete da promenite te dozvole u Podešavanjima na uređaju &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g>&lt;/strong&gt;.&lt;/p&gt;"</string>
- <string name="vendor_icon_description" msgid="4445875290032225965">"Ikona aplikacije"</string>
- <string name="vendor_header_button_description" msgid="6566660389500630608">"Dugme za više informacija"</string>
- <string name="permission_phone" msgid="2661081078692784919">"Telefon"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string>
+ <string name="consent_back" msgid="2560683030046918882">"Назад"</string>
+ <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Апликцијама на уређају &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; дајте све дозволе као на уређају &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;То може да обухвата приступ микрофону, камери и локацији, као и другим осетљивим дозволама на уређају &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;У сваком тренутку можете да промените те дозволе у Подешавањима на уређају &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g>&lt;/strong&gt;.&lt;/p&gt;"</string>
+ <string name="vendor_icon_description" msgid="4445875290032225965">"Икона апликације"</string>
+ <string name="vendor_header_button_description" msgid="6566660389500630608">"Дугме за више информација"</string>
+ <string name="permission_phone" msgid="2661081078692784919">"Телефон"</string>
<string name="permission_sms" msgid="6337141296535774786">"SMS"</string>
- <string name="permission_contacts" msgid="3858319347208004438">"Kontakti"</string>
- <string name="permission_calendar" msgid="6805668388691290395">"Kalendar"</string>
+ <string name="permission_contacts" msgid="3858319347208004438">"Контакти"</string>
+ <string name="permission_calendar" msgid="6805668388691290395">"Календар"</string>
<!-- no translation found for permission_microphone (2152206421428732949) -->
<skip />
- <string name="permission_nearby_devices" msgid="7530973297737123481">"Uređaji u blizini"</string>
- <string name="permission_storage" msgid="6831099350839392343">"Slike i mediji"</string>
- <string name="permission_notification" msgid="693762568127741203">"Obaveštenja"</string>
- <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
+ <string name="permission_nearby_devices" msgid="7530973297737123481">"Уређаји у близини"</string>
+ <string name="permission_storage" msgid="6831099350839392343">"Слике и медији"</string>
+ <string name="permission_notification" msgid="693762568127741203">"Обавештења"</string>
+ <string name="permission_app_streaming" msgid="6009695219091526422">"Апликације"</string>
<!-- no translation found for permission_nearby_device_streaming (5868108148065023161) -->
<skip />
- <string name="permission_phone_summary" msgid="6154198036705702389">"Može da pristupa vašem broju telefona i informacijama o mreži. Neophodno za upućivanje poziva i VoIP, govornu poštu, preusmeravanje poziva i izmene evidencije poziva"</string>
+ <string name="permission_phone_summary" msgid="6154198036705702389">"Може да приступа вашем броју телефона и информацијама о мрежи. Неопходно за упућивање позива и VoIP, говорну пошту, преусмеравање позива и измене евиденције позива"</string>
<string name="permission_sms_summary" msgid="5107174184224165570"></string>
- <string name="permission_contacts_summary" msgid="7850901746005070792">"Može da čita, kreira ili menja listu kontakata, kao i da pristupa listi svih naloga koji se koriste na vašem uređaju"</string>
+ <string name="permission_contacts_summary" msgid="7850901746005070792">"Може да чита, креира или мења листу контаката, као и да приступа листи свих налога који се користе на вашем уређају"</string>
<string name="permission_calendar_summary" msgid="9070743747408808156"></string>
<!-- no translation found for permission_microphone_summary (4241354865859396558) -->
<skip />
<string name="permission_nearby_devices_summary" msgid="8587497797301075494"></string>
- <string name="permission_notification_summary" msgid="884075314530071011">"Može da čita sva obaveštenja, uključujući informacije poput kontakata, poruka i slika"</string>
- <string name="permission_app_streaming_summary" msgid="606923325679670624">"Strimujte aplikacije na telefonu"</string>
+ <string name="permission_notification_summary" msgid="884075314530071011">"Може да чита сва обавештења, укључујући информације попут контаката, порука и слика"</string>
+ <string name="permission_app_streaming_summary" msgid="606923325679670624">"Стримујте апликације на телефону"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<!-- no translation found for permission_nearby_device_streaming_summary (5776807830582725074) -->
<skip />
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index 8dab1ff86e71..7a8e6e9a9779 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -22,12 +22,9 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="summary_watch" msgid="4085794790142204006">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with your notifications and access your phone, SMS, contacts, calendar, call logs and Nearby devices permissions."</string>
<string name="summary_watch_single_device" msgid="1523091550243476756">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with these permissions:"</string>
- <!-- no translation found for profile_name_glasses (8488394059007275998) -->
- <skip />
- <!-- no translation found for summary_glasses (3195267006147355861) -->
- <skip />
- <!-- no translation found for summary_glasses_single_device (5725890636605211803) -->
- <skip />
+ <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string>
+ <string name="summary_glasses" msgid="3195267006147355861">"This app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to access your phone, SMS, contacts, microphone and Nearby devices permissions."</string>
+ <string name="summary_glasses_single_device" msgid="5725890636605211803">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with these permissions:"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</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> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
@@ -37,12 +34,9 @@
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
<string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
- <!-- no translation found for title_nearby_device_streaming (179278282547719200) -->
- <skip />
- <!-- no translation found for helper_title_nearby_device_streaming (6124438217620593669) -->
- <skip />
- <!-- no translation found for helper_summary_nearby_device_streaming (5538329403511524333) -->
- <skip />
+ <string name="title_nearby_device_streaming" msgid="179278282547719200">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to perform this action from your phone"</string>
+ <string name="helper_title_nearby_device_streaming" msgid="6124438217620593669">"Cross-device services"</string>
+ <string name="helper_summary_nearby_device_streaming" msgid="5538329403511524333">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream content to nearby devices"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
@@ -56,24 +50,20 @@
<string name="permission_sms" msgid="6337141296535774786">"SMS"</string>
<string name="permission_contacts" msgid="3858319347208004438">"Contacts"</string>
<string name="permission_calendar" msgid="6805668388691290395">"Calendar"</string>
- <!-- no translation found for permission_microphone (2152206421428732949) -->
- <skip />
+ <string name="permission_microphone" msgid="2152206421428732949">"Microphone"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Nearby devices"</string>
<string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
<string name="permission_notification" msgid="693762568127741203">"Notifications"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
- <!-- no translation found for permission_nearby_device_streaming (5868108148065023161) -->
- <skip />
+ <string name="permission_nearby_device_streaming" msgid="5868108148065023161">"Nearby device streaming"</string>
<string name="permission_phone_summary" msgid="6154198036705702389">"Can access your phone number and network info. Required for making calls and VoIP, voicemail, call redirect and editing call logs"</string>
<string name="permission_sms_summary" msgid="5107174184224165570"></string>
<string name="permission_contacts_summary" msgid="7850901746005070792">"Can read, create or edit our contact list, as well as access the list of all accounts used on your device"</string>
<string name="permission_calendar_summary" msgid="9070743747408808156"></string>
- <!-- no translation found for permission_microphone_summary (4241354865859396558) -->
- <skip />
+ <string name="permission_microphone_summary" msgid="4241354865859396558">"Can record audio using the microphone"</string>
<string name="permission_nearby_devices_summary" msgid="8587497797301075494"></string>
<string name="permission_notification_summary" msgid="884075314530071011">"Can read all notifications, including information like contacts, messages and photos"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream your phone’s apps"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
- <!-- no translation found for permission_nearby_device_streaming_summary (5776807830582725074) -->
- <skip />
+ <string name="permission_nearby_device_streaming_summary" msgid="5776807830582725074">"Stream content to a nearby device"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
index 9f1b51bca29e..f555ce03131e 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -22,12 +22,9 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="summary_watch" msgid="4085794790142204006">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions."</string>
<string name="summary_watch_single_device" msgid="1523091550243476756">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with these permissions:"</string>
- <!-- no translation found for profile_name_glasses (8488394059007275998) -->
- <skip />
- <!-- no translation found for summary_glasses (3195267006147355861) -->
- <skip />
- <!-- no translation found for summary_glasses_single_device (5725890636605211803) -->
- <skip />
+ <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string>
+ <string name="summary_glasses" msgid="3195267006147355861">"This app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to access your Phone, SMS, Contacts, Microphone and Nearby devices permissions."</string>
+ <string name="summary_glasses_single_device" msgid="5725890636605211803">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with these permissions:"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</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> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
@@ -37,12 +34,9 @@
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
<string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media, and notifications"</string>
- <!-- no translation found for title_nearby_device_streaming (179278282547719200) -->
- <skip />
- <!-- no translation found for helper_title_nearby_device_streaming (6124438217620593669) -->
- <skip />
- <!-- no translation found for helper_summary_nearby_device_streaming (5538329403511524333) -->
- <skip />
+ <string name="title_nearby_device_streaming" msgid="179278282547719200">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to perform this action from your phone"</string>
+ <string name="helper_title_nearby_device_streaming" msgid="6124438217620593669">"Cross-device services"</string>
+ <string name="helper_summary_nearby_device_streaming" msgid="5538329403511524333">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream content to nearby devices"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
@@ -56,24 +50,20 @@
<string name="permission_sms" msgid="6337141296535774786">"SMS"</string>
<string name="permission_contacts" msgid="3858319347208004438">"Contacts"</string>
<string name="permission_calendar" msgid="6805668388691290395">"Calendar"</string>
- <!-- no translation found for permission_microphone (2152206421428732949) -->
- <skip />
+ <string name="permission_microphone" msgid="2152206421428732949">"Microphone"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Nearby devices"</string>
<string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
<string name="permission_notification" msgid="693762568127741203">"Notifications"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
- <!-- no translation found for permission_nearby_device_streaming (5868108148065023161) -->
- <skip />
+ <string name="permission_nearby_device_streaming" msgid="5868108148065023161">"Nearby Device Streaming"</string>
<string name="permission_phone_summary" msgid="6154198036705702389">"Can access your phone number and network info. Required for making calls and VoIP, voicemail, call redirect, and editing call logs"</string>
<string name="permission_sms_summary" msgid="5107174184224165570"></string>
<string name="permission_contacts_summary" msgid="7850901746005070792">"Can read, create, or edit our contact list, as well as access the list of all accounts used on your device"</string>
<string name="permission_calendar_summary" msgid="9070743747408808156"></string>
- <!-- no translation found for permission_microphone_summary (4241354865859396558) -->
- <skip />
+ <string name="permission_microphone_summary" msgid="4241354865859396558">"Can record audio using the microphone"</string>
<string name="permission_nearby_devices_summary" msgid="8587497797301075494"></string>
<string name="permission_notification_summary" msgid="884075314530071011">"Can read all notifications, including information like contacts, messages, and photos"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream your phone’s apps"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
- <!-- no translation found for permission_nearby_device_streaming_summary (5776807830582725074) -->
- <skip />
+ <string name="permission_nearby_device_streaming_summary" msgid="5776807830582725074">"Stream content to a nearby device"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index 8dab1ff86e71..7a8e6e9a9779 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -22,12 +22,9 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="summary_watch" msgid="4085794790142204006">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with your notifications and access your phone, SMS, contacts, calendar, call logs and Nearby devices permissions."</string>
<string name="summary_watch_single_device" msgid="1523091550243476756">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with these permissions:"</string>
- <!-- no translation found for profile_name_glasses (8488394059007275998) -->
- <skip />
- <!-- no translation found for summary_glasses (3195267006147355861) -->
- <skip />
- <!-- no translation found for summary_glasses_single_device (5725890636605211803) -->
- <skip />
+ <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string>
+ <string name="summary_glasses" msgid="3195267006147355861">"This app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to access your phone, SMS, contacts, microphone and Nearby devices permissions."</string>
+ <string name="summary_glasses_single_device" msgid="5725890636605211803">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with these permissions:"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</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> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
@@ -37,12 +34,9 @@
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
<string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
- <!-- no translation found for title_nearby_device_streaming (179278282547719200) -->
- <skip />
- <!-- no translation found for helper_title_nearby_device_streaming (6124438217620593669) -->
- <skip />
- <!-- no translation found for helper_summary_nearby_device_streaming (5538329403511524333) -->
- <skip />
+ <string name="title_nearby_device_streaming" msgid="179278282547719200">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to perform this action from your phone"</string>
+ <string name="helper_title_nearby_device_streaming" msgid="6124438217620593669">"Cross-device services"</string>
+ <string name="helper_summary_nearby_device_streaming" msgid="5538329403511524333">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream content to nearby devices"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
@@ -56,24 +50,20 @@
<string name="permission_sms" msgid="6337141296535774786">"SMS"</string>
<string name="permission_contacts" msgid="3858319347208004438">"Contacts"</string>
<string name="permission_calendar" msgid="6805668388691290395">"Calendar"</string>
- <!-- no translation found for permission_microphone (2152206421428732949) -->
- <skip />
+ <string name="permission_microphone" msgid="2152206421428732949">"Microphone"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Nearby devices"</string>
<string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
<string name="permission_notification" msgid="693762568127741203">"Notifications"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
- <!-- no translation found for permission_nearby_device_streaming (5868108148065023161) -->
- <skip />
+ <string name="permission_nearby_device_streaming" msgid="5868108148065023161">"Nearby device streaming"</string>
<string name="permission_phone_summary" msgid="6154198036705702389">"Can access your phone number and network info. Required for making calls and VoIP, voicemail, call redirect and editing call logs"</string>
<string name="permission_sms_summary" msgid="5107174184224165570"></string>
<string name="permission_contacts_summary" msgid="7850901746005070792">"Can read, create or edit our contact list, as well as access the list of all accounts used on your device"</string>
<string name="permission_calendar_summary" msgid="9070743747408808156"></string>
- <!-- no translation found for permission_microphone_summary (4241354865859396558) -->
- <skip />
+ <string name="permission_microphone_summary" msgid="4241354865859396558">"Can record audio using the microphone"</string>
<string name="permission_nearby_devices_summary" msgid="8587497797301075494"></string>
<string name="permission_notification_summary" msgid="884075314530071011">"Can read all notifications, including information like contacts, messages and photos"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream your phone’s apps"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
- <!-- no translation found for permission_nearby_device_streaming_summary (5776807830582725074) -->
- <skip />
+ <string name="permission_nearby_device_streaming_summary" msgid="5776807830582725074">"Stream content to a nearby device"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index 8dab1ff86e71..7a8e6e9a9779 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -22,12 +22,9 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="summary_watch" msgid="4085794790142204006">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with your notifications and access your phone, SMS, contacts, calendar, call logs and Nearby devices permissions."</string>
<string name="summary_watch_single_device" msgid="1523091550243476756">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with these permissions:"</string>
- <!-- no translation found for profile_name_glasses (8488394059007275998) -->
- <skip />
- <!-- no translation found for summary_glasses (3195267006147355861) -->
- <skip />
- <!-- no translation found for summary_glasses_single_device (5725890636605211803) -->
- <skip />
+ <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string>
+ <string name="summary_glasses" msgid="3195267006147355861">"This app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to access your phone, SMS, contacts, microphone and Nearby devices permissions."</string>
+ <string name="summary_glasses_single_device" msgid="5725890636605211803">"The app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with these permissions:"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</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> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
@@ -37,12 +34,9 @@
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
<string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
- <!-- no translation found for title_nearby_device_streaming (179278282547719200) -->
- <skip />
- <!-- no translation found for helper_title_nearby_device_streaming (6124438217620593669) -->
- <skip />
- <!-- no translation found for helper_summary_nearby_device_streaming (5538329403511524333) -->
- <skip />
+ <string name="title_nearby_device_streaming" msgid="179278282547719200">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to perform this action from your phone"</string>
+ <string name="helper_title_nearby_device_streaming" msgid="6124438217620593669">"Cross-device services"</string>
+ <string name="helper_summary_nearby_device_streaming" msgid="5538329403511524333">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream content to nearby devices"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
@@ -56,24 +50,20 @@
<string name="permission_sms" msgid="6337141296535774786">"SMS"</string>
<string name="permission_contacts" msgid="3858319347208004438">"Contacts"</string>
<string name="permission_calendar" msgid="6805668388691290395">"Calendar"</string>
- <!-- no translation found for permission_microphone (2152206421428732949) -->
- <skip />
+ <string name="permission_microphone" msgid="2152206421428732949">"Microphone"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Nearby devices"</string>
<string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
<string name="permission_notification" msgid="693762568127741203">"Notifications"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
- <!-- no translation found for permission_nearby_device_streaming (5868108148065023161) -->
- <skip />
+ <string name="permission_nearby_device_streaming" msgid="5868108148065023161">"Nearby device streaming"</string>
<string name="permission_phone_summary" msgid="6154198036705702389">"Can access your phone number and network info. Required for making calls and VoIP, voicemail, call redirect and editing call logs"</string>
<string name="permission_sms_summary" msgid="5107174184224165570"></string>
<string name="permission_contacts_summary" msgid="7850901746005070792">"Can read, create or edit our contact list, as well as access the list of all accounts used on your device"</string>
<string name="permission_calendar_summary" msgid="9070743747408808156"></string>
- <!-- no translation found for permission_microphone_summary (4241354865859396558) -->
- <skip />
+ <string name="permission_microphone_summary" msgid="4241354865859396558">"Can record audio using the microphone"</string>
<string name="permission_nearby_devices_summary" msgid="8587497797301075494"></string>
<string name="permission_notification_summary" msgid="884075314530071011">"Can read all notifications, including information like contacts, messages and photos"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream your phone’s apps"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
- <!-- no translation found for permission_nearby_device_streaming_summary (5776807830582725074) -->
- <skip />
+ <string name="permission_nearby_device_streaming_summary" msgid="5776807830582725074">"Stream content to a nearby device"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index b45d0d420934..ef29215e3d62 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -22,12 +22,9 @@
<string name="chooser_title" msgid="2262294130493605839">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎Choose a ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to be managed by &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
<string name="summary_watch" msgid="4085794790142204006">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎The app is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions.‎‏‎‎‏‎"</string>
<string name="summary_watch_single_device" msgid="1523091550243476756">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎The app is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ will be allowed to interact with these permissions:‎‏‎‎‏‎"</string>
- <!-- no translation found for profile_name_glasses (8488394059007275998) -->
- <skip />
- <!-- no translation found for summary_glasses (3195267006147355861) -->
- <skip />
- <!-- no translation found for summary_glasses_single_device (5725890636605211803) -->
- <skip />
+ <string name="profile_name_glasses" msgid="8488394059007275998">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‎glasses‎‏‎‎‏‎"</string>
+ <string name="summary_glasses" msgid="3195267006147355861">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎This app is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ will be allowed to access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.‎‏‎‎‏‎"</string>
+ <string name="summary_glasses_single_device" msgid="5725890636605211803">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‏‎The app is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ will be allowed to interact with these permissions:‎‏‎‎‏‎"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to access this information from your phone‎‏‎‎‏‎"</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>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to stream apps between your devices‎‏‎‎‏‎"</string>
@@ -37,12 +34,9 @@
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎Google Play services‎‏‎‎‏‎"</string>
<string name="helper_summary_computer" msgid="9050724687678157852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to access your phone’s photos, media, and notifications‎‏‎‎‏‎"</string>
- <!-- no translation found for title_nearby_device_streaming (179278282547719200) -->
- <skip />
- <!-- no translation found for helper_title_nearby_device_streaming (6124438217620593669) -->
- <skip />
- <!-- no translation found for helper_summary_nearby_device_streaming (5538329403511524333) -->
- <skip />
+ <string name="title_nearby_device_streaming" msgid="179278282547719200">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to perform this action from your phone‎‏‎‎‏‎"</string>
+ <string name="helper_title_nearby_device_streaming" msgid="6124438217620593669">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎Cross-device services‎‏‎‎‏‎"</string>
+ <string name="helper_summary_nearby_device_streaming" msgid="5538329403511524333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to stream content to nearby devices‎‏‎‎‏‎"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎device‎‏‎‎‏‎"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎Allow‎‏‎‎‏‎"</string>
@@ -56,24 +50,20 @@
<string name="permission_sms" msgid="6337141296535774786">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‏‎‎‎‎‏‎‎SMS‎‏‎‎‏‎"</string>
<string name="permission_contacts" msgid="3858319347208004438">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎Contacts‎‏‎‎‏‎"</string>
<string name="permission_calendar" msgid="6805668388691290395">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎Calendar‎‏‎‎‏‎"</string>
- <!-- no translation found for permission_microphone (2152206421428732949) -->
- <skip />
+ <string name="permission_microphone" msgid="2152206421428732949">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎Microphone‎‏‎‎‏‎"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‎‎‏‎Nearby devices‎‏‎‎‏‎"</string>
<string name="permission_storage" msgid="6831099350839392343">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎Photos and media‎‏‎‎‏‎"</string>
<string name="permission_notification" msgid="693762568127741203">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎Notifications‎‏‎‎‏‎"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‏‏‎‎Apps‎‏‎‎‏‎"</string>
- <!-- no translation found for permission_nearby_device_streaming (5868108148065023161) -->
- <skip />
+ <string name="permission_nearby_device_streaming" msgid="5868108148065023161">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‎‏‎Nearby Device Streaming‎‏‎‎‏‎"</string>
<string name="permission_phone_summary" msgid="6154198036705702389">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎Can access your phone number and network info. Required for making calls and VoIP, voicemail, call redirect, and editing call logs‎‏‎‎‏‎"</string>
<string name="permission_sms_summary" msgid="5107174184224165570"></string>
<string name="permission_contacts_summary" msgid="7850901746005070792">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‎Can read, create, or edit our contact list, as well as access the list of all accounts used on your device‎‏‎‎‏‎"</string>
<string name="permission_calendar_summary" msgid="9070743747408808156"></string>
- <!-- no translation found for permission_microphone_summary (4241354865859396558) -->
- <skip />
+ <string name="permission_microphone_summary" msgid="4241354865859396558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎Can record audio using the microphone‎‏‎‎‏‎"</string>
<string name="permission_nearby_devices_summary" msgid="8587497797301075494"></string>
<string name="permission_notification_summary" msgid="884075314530071011">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎Can read all notifications, including information like contacts, messages, and photos‎‏‎‎‏‎"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎Stream your phone’s apps‎‏‎‎‏‎"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
- <!-- no translation found for permission_nearby_device_streaming_summary (5776807830582725074) -->
- <skip />
+ <string name="permission_nearby_device_streaming_summary" msgid="5776807830582725074">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‎Stream content to a nearby device‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index 635b746283c9..2844208d8a28 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -22,12 +22,9 @@
<string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="summary_watch" msgid="4085794790142204006">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g> จะได้รับอนุญาตให้โต้ตอบกับการแจ้งเตือนและได้รับสิทธิ์เข้าถึงโทรศัพท์, SMS, รายชื่อติดต่อ, ปฏิทิน, บันทึกการโทร และอุปกรณ์ที่อยู่ใกล้เคียง"</string>
<string name="summary_watch_single_device" msgid="1523091550243476756">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g> จะได้รับอนุญาตให้โต้ตอบกับสิทธิ์เหล่านี้"</string>
- <!-- no translation found for profile_name_glasses (8488394059007275998) -->
- <skip />
- <!-- no translation found for summary_glasses (3195267006147355861) -->
- <skip />
- <!-- no translation found for summary_glasses_single_device (5725890636605211803) -->
- <skip />
+ <string name="profile_name_glasses" msgid="8488394059007275998">"แว่นตา"</string>
+ <string name="summary_glasses" msgid="3195267006147355861">"ต้องใช้แอปนี้ในการจัดการ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g> จะได้รับสิทธิ์เข้าถึงโทรศัพท์, SMS, รายชื่อติดต่อ, ไมโครโฟน และอุปกรณ์ที่อยู่ใกล้เคียง"</string>
+ <string name="summary_glasses_single_device" msgid="5725890636605211803">"ต้องใช้แอปนี้ในการจัดการ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g> จะได้รับอนุญาตให้โต้ตอบกับสิทธิ์เหล่านี้"</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>
@@ -37,12 +34,9 @@
<string name="summary_computer" msgid="3798467601598297062"></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>
- <!-- no translation found for title_nearby_device_streaming (179278282547719200) -->
- <skip />
- <!-- no translation found for helper_title_nearby_device_streaming (6124438217620593669) -->
- <skip />
- <!-- no translation found for helper_summary_nearby_device_streaming (5538329403511524333) -->
- <skip />
+ <string name="title_nearby_device_streaming" msgid="179278282547719200">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ทำงานนี้จากโทรศัพท์"</string>
+ <string name="helper_title_nearby_device_streaming" msgid="6124438217620593669">"บริการหลายอุปกรณ์"</string>
+ <string name="helper_summary_nearby_device_streaming" msgid="5538329403511524333">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> เพื่อสตรีมเนื้อหาไปยังอุปกรณ์ที่อยู่ใกล้เคียง"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string>
@@ -56,24 +50,20 @@
<string name="permission_sms" msgid="6337141296535774786">"SMS"</string>
<string name="permission_contacts" msgid="3858319347208004438">"รายชื่อติดต่อ"</string>
<string name="permission_calendar" msgid="6805668388691290395">"ปฏิทิน"</string>
- <!-- no translation found for permission_microphone (2152206421428732949) -->
- <skip />
+ <string name="permission_microphone" msgid="2152206421428732949">"ไมโครโฟน"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"อุปกรณ์ที่อยู่ใกล้เคียง"</string>
<string name="permission_storage" msgid="6831099350839392343">"รูปภาพและสื่อ"</string>
<string name="permission_notification" msgid="693762568127741203">"การแจ้งเตือน"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"แอป"</string>
- <!-- no translation found for permission_nearby_device_streaming (5868108148065023161) -->
- <skip />
+ <string name="permission_nearby_device_streaming" msgid="5868108148065023161">"การสตรีมไปยังอุปกรณ์ที่อยู่ใกล้เคียง"</string>
<string name="permission_phone_summary" msgid="6154198036705702389">"สามารถเข้าถึงหมายเลขโทรศัพท์และข้อมูลเครือข่ายของคุณ จำเป็นสำหรับการโทรและ VoIP, ข้อความเสียง, การเปลี่ยนเส้นทางการโทร และการแก้ไขบันทึกการโทร"</string>
<string name="permission_sms_summary" msgid="5107174184224165570"></string>
<string name="permission_contacts_summary" msgid="7850901746005070792">"สามารถอ่าน สร้าง หรือแก้ไขข้อมูลรายชื่อติดต่อของเรา รวมทั้งเข้าถึงข้อมูลรายชื่อติดต่อของทุกบัญชีที่ใช้ในอุปกรณ์ของคุณ"</string>
<string name="permission_calendar_summary" msgid="9070743747408808156"></string>
- <!-- no translation found for permission_microphone_summary (4241354865859396558) -->
- <skip />
+ <string name="permission_microphone_summary" msgid="4241354865859396558">"บันทึกเสียงโดยใช้ไมโครโฟนได้"</string>
<string name="permission_nearby_devices_summary" msgid="8587497797301075494"></string>
<string name="permission_notification_summary" msgid="884075314530071011">"สามารถอ่านการแจ้งเตือนทั้งหมด รวมถึงข้อมูลอย่างรายชื่อติดต่อ ข้อความ และรูปภาพ"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"สตรีมแอปของโทรศัพท์คุณ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
- <!-- no translation found for permission_nearby_device_streaming_summary (5776807830582725074) -->
- <skip />
+ <string name="permission_nearby_device_streaming_summary" msgid="5776807830582725074">"สตรีมเนื้อหาไปยังอุปกรณ์ที่อยู่ใกล้เคียง"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index e2f71b570e50..e4c71e79c62d 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -22,12 +22,9 @@
<string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="summary_watch" msgid="4085794790142204006">"Kailangan ang app para mapamahalaan ang iyong <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Papayagan ang <xliff:g id="APP_NAME">%2$s</xliff:g> na makipag-ugnayan sa mga notification mo at i-access ang iyong pahintulot sa Telepono, SMS, Mga Contact, Kalendaryo, Log ng mga tawag, at Mga kalapit na device."</string>
<string name="summary_watch_single_device" msgid="1523091550243476756">"Kailangan ang app para mapamahalaan ang iyong <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Papayagan ang <xliff:g id="APP_NAME">%2$s</xliff:g> na makipag-ugnayan sa mga pahintulot na ito:"</string>
- <!-- no translation found for profile_name_glasses (8488394059007275998) -->
- <skip />
- <!-- no translation found for summary_glasses (3195267006147355861) -->
- <skip />
- <!-- no translation found for summary_glasses_single_device (5725890636605211803) -->
- <skip />
+ <string name="profile_name_glasses" msgid="8488394059007275998">"salamin"</string>
+ <string name="summary_glasses" msgid="3195267006147355861">"Kailangan ang app na ito para mapamahalaan ang iyong <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Papayagan ang <xliff:g id="APP_NAME">%2$s</xliff:g> na i-access ang iyong mga pahintulot sa Telepono, SMS, Mga Contact, Mikropono, at Mga kalapit na device."</string>
+ <string name="summary_glasses_single_device" msgid="5725890636605211803">"Kailangan ang app para mapamahalaan ang iyong <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Papayagan ang <xliff:g id="APP_NAME">%2$s</xliff:g> na makipag-ugnayan sa mga pahintulot na ito:"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na i-access ang impormasyong ito sa iyong telepono"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Mga cross-device na serbisyo"</string>
<string name="helper_summary_app_streaming" msgid="5977509499890099">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para mag-stream ng mga app sa pagitan ng mga device mo"</string>
@@ -37,12 +34,9 @@
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Mga serbisyo ng Google Play"</string>
<string name="helper_summary_computer" msgid="9050724687678157852">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para i-access ang mga larawan, media, at notification ng telepono mo"</string>
- <!-- no translation found for title_nearby_device_streaming (179278282547719200) -->
- <skip />
- <!-- no translation found for helper_title_nearby_device_streaming (6124438217620593669) -->
- <skip />
- <!-- no translation found for helper_summary_nearby_device_streaming (5538329403511524333) -->
- <skip />
+ <string name="title_nearby_device_streaming" msgid="179278282547719200">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na gawin ang pagkilos na ito mula sa iyong telepono"</string>
+ <string name="helper_title_nearby_device_streaming" msgid="6124438217620593669">"Mga cross-device na serbisyo"</string>
+ <string name="helper_summary_nearby_device_streaming" msgid="5538329403511524333">"Humihiling ng pahintulot ang <xliff:g id="APP_NAME">%1$s</xliff:g> sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para mag-stream ng content sa mga kalapit na device"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Payagan"</string>
@@ -56,24 +50,20 @@
<string name="permission_sms" msgid="6337141296535774786">"SMS"</string>
<string name="permission_contacts" msgid="3858319347208004438">"Mga Contact"</string>
<string name="permission_calendar" msgid="6805668388691290395">"Calendar"</string>
- <!-- no translation found for permission_microphone (2152206421428732949) -->
- <skip />
+ <string name="permission_microphone" msgid="2152206421428732949">"Mikropono"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Mga kalapit na device"</string>
<string name="permission_storage" msgid="6831099350839392343">"Mga larawan at media"</string>
<string name="permission_notification" msgid="693762568127741203">"Mga Notification"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Mga App"</string>
- <!-- no translation found for permission_nearby_device_streaming (5868108148065023161) -->
- <skip />
+ <string name="permission_nearby_device_streaming" msgid="5868108148065023161">"Streaming sa Kalapit na Device"</string>
<string name="permission_phone_summary" msgid="6154198036705702389">"Naa-access ang iyong numero ng telepono at impormasyon ng network. Kinakailangan para sa mga pagtawag at VoIP, voicemail, pag-redirect ng tawag, at pag-edit ng mga log ng tawag"</string>
<string name="permission_sms_summary" msgid="5107174184224165570"></string>
<string name="permission_contacts_summary" msgid="7850901746005070792">"Nakaka-read, nakakagawa, o nakakapag-edit ng aming listahan ng contact, pati na rin nakaka-access ng listahan ng lahat ng account na ginamit sa iyong device"</string>
<string name="permission_calendar_summary" msgid="9070743747408808156"></string>
- <!-- no translation found for permission_microphone_summary (4241354865859396558) -->
- <skip />
+ <string name="permission_microphone_summary" msgid="4241354865859396558">"Puwedeng mag-record ng audio gamit ang mikropono"</string>
<string name="permission_nearby_devices_summary" msgid="8587497797301075494"></string>
<string name="permission_notification_summary" msgid="884075314530071011">"Magbasa ng lahat ng notification, kabilang ang impormasyon gaya ng mga contact, mensahe, at larawan"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"I-stream ang mga app ng iyong telepono"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
- <!-- no translation found for permission_nearby_device_streaming_summary (5776807830582725074) -->
- <skip />
+ <string name="permission_nearby_device_streaming_summary" msgid="5776807830582725074">"Mag-stream ng content sa kalapit na device"</string>
</resources>
diff --git a/packages/CredentialManager/res/values-af/strings.xml b/packages/CredentialManager/res/values-af/strings.xml
index cbf4542d2208..c42af9c9c487 100644
--- a/packages/CredentialManager/res/values-af/strings.xml
+++ b/packages/CredentialManager/res/values-af/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Ander wagwoordbestuurders"</string>
<string name="close_sheet" msgid="1393792015338908262">"Maak sigblad toe"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gaan terug na die vorige bladsy"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Maak die Eiebewysbestuurder se handelingvoorstel toe wat onderaan die skerm verskyn"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gebruik jou gestoorde wagwoordsleutel vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gebruik jou gestoorde aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Kies ’n gestoorde aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-am/strings.xml b/packages/CredentialManager/res/values-am/strings.xml
index e20079551c68..f0705fb87379 100644
--- a/packages/CredentialManager/res/values-am/strings.xml
+++ b/packages/CredentialManager/res/values-am/strings.xml
@@ -8,21 +8,15 @@
<string name="string_save_to_another_place" msgid="7590325934591079193">"ወደ ሌላ ቦታ ያስቀምጡ"</string>
<string name="string_use_another_device" msgid="8754514926121520445">"ሌላ መሣሪያ ይጠቀሙ"</string>
<string name="string_save_to_another_device" msgid="1959562542075194458">"ወደ ሌላ መሣሪያ ያስቀምጡ"</string>
- <!-- no translation found for passkey_creation_intro_title (4251037543787718844) -->
- <skip />
- <!-- no translation found for passkey_creation_intro_body_password (312712407571126228) -->
- <skip />
- <!-- no translation found for passkey_creation_intro_body_fingerprint (691816235541508203) -->
- <skip />
- <!-- no translation found for passkey_creation_intro_body_device (477121861162321129) -->
- <skip />
+ <string name="passkey_creation_intro_title" msgid="4251037543787718844">"በይለፍ ቃል ይበልጥ ደህንነቱ የተጠበቀ"</string>
+ <string name="passkey_creation_intro_body_password" msgid="312712407571126228">"ውስብስብ የይለፍ ቃላትን መፍጠር ወይም ማስታወስ አያስፈልግም"</string>
+ <string name="passkey_creation_intro_body_fingerprint" msgid="691816235541508203">"ልዩ የይለፍ ቁልፍ ለመፍጠር የእርስዎን የጣት አሻራ፣ ፊት ወይም ማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
+ <string name="passkey_creation_intro_body_device" msgid="477121861162321129">"የይለፍ ቁልፎች የይለፍ ቃል አስተዳዳሪ ላይ ይቀመጣሉ፣ ስለዚህ በሌሎች መሣሪያዎች ላይ መግባት ይችላሉ"</string>
<string name="choose_provider_title" msgid="7245243990139698508">"የት <xliff:g id="CREATETYPES">%1$s</xliff:g> እንደሚሆን ይምረጡ"</string>
- <!-- no translation found for create_your_passkeys (8901224153607590596) -->
- <skip />
+ <string name="create_your_passkeys" msgid="8901224153607590596">"የይለፍ ቁልፎችዎን ይፍጠሩ"</string>
<string name="save_your_password" msgid="6597736507991704307">"የይለፍ ቃልዎን ያስቀምጡ"</string>
<string name="save_your_sign_in_info" msgid="7213978049817076882">"የመግቢያ መረጃዎን ያስቀምጡ"</string>
- <!-- no translation found for choose_provider_body (8045759834416308059) -->
- <skip />
+ <string name="choose_provider_body" msgid="8045759834416308059">"የእርስዎን የይለፍ ቃላት እና የይለፍ ቁልፎች ለማስቀመጥ እና በሚቀጥለው ጊዜ በበለጠ ፍጥነት ለመግባት ነባሪ የሚስጥር ቁልፍ አስተዳዳሪ ያቀናብሩ።"</string>
<string name="choose_create_option_passkey_title" msgid="4146408187146573131">"በ<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ውስጥ የይለፍ ቁልፍ ይፈጠር?"</string>
<string name="choose_create_option_password_title" msgid="8812546498357380545">"የይለፍ ቃልዎ ወደ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ይቀመጥ?"</string>
<string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"የመግቢያ መረጃዎ ወደ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ይቀመጥ?"</string>
@@ -30,17 +24,12 @@
<string name="passkey" msgid="632353688396759522">"የይለፍ ቁልፍ"</string>
<string name="password" msgid="6738570945182936667">"የይለፍ ቃል"</string>
<string name="sign_ins" msgid="4710739369149469208">"መግቢያዎች"</string>
- <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
- <skip />
- <!-- no translation found for save_password_to_title (3450480045270186421) -->
- <skip />
- <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
- <skip />
- <!-- no translation found for create_passkey_in_other_device_title (6372952459932674632) -->
- <skip />
+ <string name="create_passkey_in_title" msgid="2714306562710897785">"በሚከተለው ውስጥ የይለፍ ቁልፍ ይፈጠሩ"</string>
+ <string name="save_password_to_title" msgid="3450480045270186421">"የይለፍ ቃል አስቀምጥ ወደ"</string>
+ <string name="save_sign_in_to_title" msgid="8328143607671760232">"መግቢያን አስቀምጥ ወደ"</string>
+ <string name="create_passkey_in_other_device_title" msgid="6372952459932674632">"በሌላ መሣሪያ የይለፍ ቁልፍ ይፈጠር?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ለሁሉም መግቢያዎችዎ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ን ይጠቀሙ?"</string>
- <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="6560593199974037820">"ይህ የይለፍ ቃል አስተዳዳሪ በቀላሉ እንዲገቡ ለማገዝ የእርስዎን የይለፍ ቃሎች እና የይለፍ ቁልፎች ያከማቻል።"</string>
<string name="set_as_default" msgid="4415328591568654603">"እንደ ነባሪ ያዋቅሩ"</string>
<string name="use_once" msgid="9027366575315399714">"አንዴ ይጠቀሙ"</string>
<string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> የይለፍ ቃሎች፣ <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> የይለፍ ቁልፎች"</string>
diff --git a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
index 414e02a00e4d..3680059dd23c 100644
--- a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
@@ -1,58 +1,58 @@
<?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="app_name" msgid="4539824758261855508">"Menadžer akreditiva"</string>
- <string name="string_cancel" msgid="6369133483981306063">"Otkaži"</string>
- <string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
- <string name="string_create_in_another_place" msgid="1033635365843437603">"Napravi na drugom mestu"</string>
- <string name="string_save_to_another_place" msgid="7590325934591079193">"Sačuvaj na drugom mestu"</string>
- <string name="string_use_another_device" msgid="8754514926121520445">"Koristi drugi uređaj"</string>
- <string name="string_save_to_another_device" msgid="1959562542075194458">"Sačuvaj na drugi uređaj"</string>
- <string name="passkey_creation_intro_title" msgid="4251037543787718844">"Bezbednije uz pristupne kodove"</string>
- <string name="passkey_creation_intro_body_password" msgid="312712407571126228">"Nema potrebe da pravite ili pamtite složene lozinke"</string>
- <string name="passkey_creation_intro_body_fingerprint" msgid="691816235541508203">"Koristite otisak prsta, lice ili zaključavanje ekrana da biste napravili jedinstveni pristupni kôd"</string>
- <string name="passkey_creation_intro_body_device" msgid="477121861162321129">"Pristupni kodovi se čuvaju u menadžeru lozinki da biste mogli da se prijavljujete na drugim uređajima"</string>
- <string name="choose_provider_title" msgid="7245243990139698508">"Odaberite lokaciju za: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
- <string name="create_your_passkeys" msgid="8901224153607590596">"napravite pristupne kodove"</string>
- <string name="save_your_password" msgid="6597736507991704307">"sačuvajte lozinku"</string>
- <string name="save_your_sign_in_info" msgid="7213978049817076882">"sačuvajte podatke o prijavljivanju"</string>
- <string name="choose_provider_body" msgid="8045759834416308059">"Podesite podrazumevani menadžer lozinki da biste sačuvali lozinke i pristupne kodove i sledeći put se prijavili brže."</string>
- <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Želite da napravite pristupni kôd kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="8812546498357380545">"Želite da sačuvate lozinku kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Želite da sačuvate podatke o prijavljivanju kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_description" msgid="4419171903963100257">"Možete da koristite tip domena <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> na bilo kom uređaju. Čuva se kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> za: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
- <string name="passkey" msgid="632353688396759522">"pristupni kôd"</string>
- <string name="password" msgid="6738570945182936667">"lozinka"</string>
- <string name="sign_ins" msgid="4710739369149469208">"prijavljivanja"</string>
- <string name="create_passkey_in_title" msgid="2714306562710897785">"Napravite pristupni kôd u:"</string>
- <string name="save_password_to_title" msgid="3450480045270186421">"Sačuvajte lozinku na:"</string>
- <string name="save_sign_in_to_title" msgid="8328143607671760232">"Sačuvajte podatke o prijavljivanju na:"</string>
- <string name="create_passkey_in_other_device_title" msgid="6372952459932674632">"Želite da napravite pristupni kôd na drugom uređaju?"</string>
- <string name="use_provider_for_all_title" msgid="4201020195058980757">"Želite da za sva prijavljivanja koristite: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
- <string name="use_provider_for_all_description" msgid="6560593199974037820">"Ovaj menadžer lozinki će čuvati lozinke i pristupne kodove da biste se lako prijavljivali."</string>
- <string name="set_as_default" msgid="4415328591568654603">"Podesi kao podrazumevano"</string>
- <string name="use_once" msgid="9027366575315399714">"Koristi jednom"</string>
- <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, pristupnih kodova:<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
- <string name="more_options_usage_passwords" msgid="1632047277723187813">"Lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
- <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Pristupnih kodova: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
- <string name="passkey_before_subtitle" msgid="2448119456208647444">"Pristupni kôd"</string>
- <string name="another_device" msgid="5147276802037801217">"Drugi uređaj"</string>
- <string name="other_password_manager" msgid="565790221427004141">"Drugi menadžeri lozinki"</string>
- <string name="close_sheet" msgid="1393792015338908262">"Zatvorite tabelu"</string>
- <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vratite se na prethodnu stranicu"</string>
+ <string name="app_name" msgid="4539824758261855508">"Менаџер акредитива"</string>
+ <string name="string_cancel" msgid="6369133483981306063">"Откажи"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Настави"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Направи на другом месту"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Сачувај на другом месту"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Користи други уређај"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Сачувај на други уређај"</string>
+ <string name="passkey_creation_intro_title" msgid="4251037543787718844">"Безбедније уз приступне кодове"</string>
+ <string name="passkey_creation_intro_body_password" msgid="312712407571126228">"Нема потребе да правите или памтите сложене лозинке"</string>
+ <string name="passkey_creation_intro_body_fingerprint" msgid="691816235541508203">"Користите отисак прста, лице или закључавање екрана да бисте направили јединствени приступни кôд"</string>
+ <string name="passkey_creation_intro_body_device" msgid="477121861162321129">"Приступни кодови се чувају у менаџеру лозинки да бисте могли да се пријављујете на другим уређајима"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Одаберите локацију за: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="create_your_passkeys" msgid="8901224153607590596">"направите приступне кодове"</string>
+ <string name="save_your_password" msgid="6597736507991704307">"сачувајте лозинку"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"сачувајте податке о пријављивању"</string>
+ <string name="choose_provider_body" msgid="8045759834416308059">"Подесите подразумевани менаџер лозинки да бисте сачували лозинке и приступне кодове и следећи пут се пријавили брже."</string>
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Желите да направите приступни кôд код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Желите да сачувате лозинку код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Желите да сачувате податке о пријављивању код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Можете да користите тип домена <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> на било ком уређају. Чува се код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> за: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"приступни кôд"</string>
+ <string name="password" msgid="6738570945182936667">"лозинка"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"пријављивања"</string>
+ <string name="create_passkey_in_title" msgid="2714306562710897785">"Направите приступни кôд у:"</string>
+ <string name="save_password_to_title" msgid="3450480045270186421">"Сачувајте лозинку на:"</string>
+ <string name="save_sign_in_to_title" msgid="8328143607671760232">"Сачувајте податке о пријављивању на:"</string>
+ <string name="create_passkey_in_other_device_title" msgid="6372952459932674632">"Желите да направите приступни кôд на другом уређају?"</string>
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Желите да за сва пријављивања користите: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="use_provider_for_all_description" msgid="6560593199974037820">"Овај менаџер лозинки ће чувати лозинке и приступне кодове да бисте се лако пријављивали."</string>
+ <string name="set_as_default" msgid="4415328591568654603">"Подеси као подразумевано"</string>
+ <string name="use_once" msgid="9027366575315399714">"Користи једном"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Лозинки: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, приступних кодова:<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Лозинки: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Приступних кодова: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <string name="passkey_before_subtitle" msgid="2448119456208647444">"Приступни кôд"</string>
+ <string name="another_device" msgid="5147276802037801217">"Други уређај"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Други менаџери лозинки"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Затворите табелу"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вратите се на претходну страницу"</string>
<!-- no translation found for accessibility_close_button (2953807735590034688) -->
<skip />
- <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite da koristite sačuvani pristupni kôd za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
- <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite da koristite sačuvane podatke za prijavljivanje za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
- <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Odaberite sačuvano prijavljivanje za: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prijavite se na drugi način"</string>
- <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, hvala"</string>
- <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Nastavi"</string>
- <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcije za prijavljivanje"</string>
- <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Za: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Menadžeri zaključanih lozinki"</string>
- <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Dodirnite da biste otključali"</string>
- <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljajte prijavljivanjima"</string>
- <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Sa drugog uređaja"</string>
- <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Koristi drugi uređaj"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Желите да користите сачувани приступни кôд за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Желите да користите сачуване податке за пријављивање за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Одаберите сачувано пријављивање за: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Пријавите се на други начин"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Не, хвала"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Настави"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Опције за пријављивање"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"За: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Менаџери закључаних лозинки"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Додирните да бисте откључали"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управљајте пријављивањима"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Са другог уређаја"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Користи други уређај"</string>
</resources>
diff --git a/packages/CredentialManager/res/values-bn/strings.xml b/packages/CredentialManager/res/values-bn/strings.xml
index f6654fc023d2..6db94ba0a2bd 100644
--- a/packages/CredentialManager/res/values-bn/strings.xml
+++ b/packages/CredentialManager/res/values-bn/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"অন্যান্য Password Manager"</string>
<string name="close_sheet" msgid="1393792015338908262">"শিট বন্ধ করুন"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"আগের পৃষ্ঠায় ফিরে যান"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"স্ক্রিনের নিচে দেখানো Credential Manager অ্যাকশন সংক্রান্ত সাজেশন বন্ধ করুন"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য আপনার সেভ করা পাসকী ব্যবহার করবেন?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য আপনার সেভ করা সাইন-ইন সম্পর্কিত ক্রেডেনশিয়াল ব্যবহার করবেন?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য সাইন-ইন করা সম্পর্কিত ক্রেডেনশিয়াল বেছে নিন"</string>
diff --git a/packages/CredentialManager/res/values-da/strings.xml b/packages/CredentialManager/res/values-da/strings.xml
index 4a1f7de29672..14d8dc096be7 100644
--- a/packages/CredentialManager/res/values-da/strings.xml
+++ b/packages/CredentialManager/res/values-da/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Andre adgangskodeadministratorer"</string>
<string name="close_sheet" msgid="1393792015338908262">"Luk arket"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tilbage til den forrige side"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Luk handlingsforslaget for Loginstyring, som vises nederst på skærmen"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vil du bruge din gemte adgangsnøgle til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vil du bruge din gemte loginmetode til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Vælg en gemt loginmetode til <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml
index 73c7eab13b7e..1a93a29882bc 100644
--- a/packages/CredentialManager/res/values-es-rUS/strings.xml
+++ b/packages/CredentialManager/res/values-es-rUS/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Otros administradores de contraseñas"</string>
<string name="close_sheet" msgid="1393792015338908262">"Cerrar hoja"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver a la página anterior"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Cierra la sugerencia de acción del Administrador de credenciales que aparece en la parte inferior de la pantalla"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"¿Quieres usar tu llave de acceso guardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"¿Quieres usar tu acceso guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Elige un acceso guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-es/strings.xml b/packages/CredentialManager/res/values-es/strings.xml
index b08839aa0215..1dd8aa7c5d90 100644
--- a/packages/CredentialManager/res/values-es/strings.xml
+++ b/packages/CredentialManager/res/values-es/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Otros gestores de contraseñas"</string>
<string name="close_sheet" msgid="1393792015338908262">"Cerrar hoja"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver a la página anterior"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Cierra la sugerencia de acción del Gestor de credenciales de la parte inferior de la pantalla"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"¿Usar la llave de acceso guardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"¿Usar el inicio de sesión guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Elige un inicio de sesión guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml
index de2ed5f49068..4350a471d562 100644
--- a/packages/CredentialManager/res/values-fr-rCA/strings.xml
+++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Autres gestionnaires de mots de passe"</string>
<string name="close_sheet" msgid="1393792015338908262">"Fermer la feuille"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Retourner à la page précédente"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Fermez l\'action suggérée par le gestionnaire d\'authentifiants qui est affichée au bas de l\'écran"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Utiliser votre clé d\'accès enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Utiliser votre connexion enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choisir une connexion enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml
index 85d3137389b8..4846eb92eb71 100644
--- a/packages/CredentialManager/res/values-hu/strings.xml
+++ b/packages/CredentialManager/res/values-hu/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Egyéb jelszókezelők"</string>
<string name="close_sheet" msgid="1393792015338908262">"Munkalap bezárása"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vissza az előző oldalra"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"A Tanúsítványkezelő képernyő alján megjelenő műveletjavaslat bezárása"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett azonosítókulcsot használni?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett bejelentkezési adatait használni?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Mentett bejelentkezési adatok választása a következő számára: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
index c51e15e1e467..57cd19b8bb92 100644
--- a/packages/CredentialManager/res/values-hy/strings.xml
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Գաղտնաբառերի այլ կառավարիչներ"</string>
<string name="close_sheet" msgid="1393792015338908262">"Փակել թերթը"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Անցնել նախորդ էջ"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Փակել Մուտքի տվյալների կառավարչի հուշումը, որը ցուցադրվում է էկրանի ներքևի հատվածում"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Օգտագործե՞լ պահված անցաբառը <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Օգտագործե՞լ մուտքի պահված տվյալները <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Ընտրեք մուտքի պահված տվյալներ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string>
diff --git a/packages/CredentialManager/res/values-in/strings.xml b/packages/CredentialManager/res/values-in/strings.xml
index fc20ad88c475..aa09da2b5ad5 100644
--- a/packages/CredentialManager/res/values-in/strings.xml
+++ b/packages/CredentialManager/res/values-in/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Pengelola sandi lainnya"</string>
<string name="close_sheet" msgid="1393792015338908262">"Tutup sheet"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kembali ke halaman sebelumnya"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Tutup saran tindakan Pengelola Kredensial yang muncul di bagian bawah layar"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gunakan kunci sandi tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gunakan info login tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pilih info login tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml
index 92992817f734..5416d51fccba 100644
--- a/packages/CredentialManager/res/values-ja/strings.xml
+++ b/packages/CredentialManager/res/values-ja/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"他のパスワード マネージャー"</string>
<string name="close_sheet" msgid="1393792015338908262">"シートを閉じます"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"前のページに戻ります"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"画面下に表示されている認証情報マネージャーのおすすめの操作を閉じます"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したパスキーを使用しますか?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報を使用しますか?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報の選択"</string>
diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml
index c8605909e52b..b2d051a4c2cd 100644
--- a/packages/CredentialManager/res/values-ka/strings.xml
+++ b/packages/CredentialManager/res/values-ka/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"პაროლების სხვა მმართველები"</string>
<string name="close_sheet" msgid="1393792015338908262">"ფურცლის დახურვა"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"წინა გვერდზე დაბრუნება"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"დახურეთ ავტორიზაციის მონაცემების მმართველის მოქმედებების შემოთავაზებები, რომლებიც ეკრანის ქვემოთ ჩნდება"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"გსურთ თქვენი დამახსოვრებული წვდომის გასაღების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"გსურთ თქვენი დამახსოვრებული სისტემაში შესვლის მონაცემების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"აირჩიეთ სისტემაში შესვლის ინფორმაცია აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-ko/strings.xml b/packages/CredentialManager/res/values-ko/strings.xml
index 7b779b957168..d6089ce7a926 100644
--- a/packages/CredentialManager/res/values-ko/strings.xml
+++ b/packages/CredentialManager/res/values-ko/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"기타 비밀번호 관리자"</string>
<string name="close_sheet" msgid="1393792015338908262">"시트 닫기"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"이전 페이지로 돌아가기"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"화면 하단에 표시되는 인증 관리자 작업 제안 닫기"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 패스키를 사용하시겠습니까?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 로그인 정보를 사용하시겠습니까?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 로그인 정보 선택"</string>
diff --git a/packages/CredentialManager/res/values-lo/strings.xml b/packages/CredentialManager/res/values-lo/strings.xml
index 5d657e34dc65..fd3a09c83ff3 100644
--- a/packages/CredentialManager/res/values-lo/strings.xml
+++ b/packages/CredentialManager/res/values-lo/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"ຕົວຈັດການລະຫັດຜ່ານອື່ນໆ"</string>
<string name="close_sheet" msgid="1393792015338908262">"ປິດຊີດ"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ກັບຄືນໄປຫາໜ້າກ່ອນໜ້ານີ້"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"ປິດການແນະນຳການດຳເນີນການຂອງຕົວຈັດການຂໍ້ມູນການເຂົ້າສູ່ລະບົບເຊິ່ງປາກົດຢູ່ລຸ່ມສຸດຂອງໜ້າຈໍ"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ໃຊ້ກະແຈຜ່ານທີ່ບັນທຶກໄວ້ຂອງທ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ໃຊ້ການເຂົ້າສູ່ລະບົບທີ່ບັນທຶກໄວ້ຂອງທ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"ເລືອກການເຂົ້າສູ່ລະບົບທີ່ບັນທຶກໄວ້ສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-lt/strings.xml b/packages/CredentialManager/res/values-lt/strings.xml
index 1b857d75c4f5..78a25774495e 100644
--- a/packages/CredentialManager/res/values-lt/strings.xml
+++ b/packages/CredentialManager/res/values-lt/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Kitos slaptažodžių tvarkyklės"</string>
<string name="close_sheet" msgid="1393792015338908262">"Uždaryti lapą"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Grįžti į ankstesnį puslapį"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Uždaryti prisijungimo duomenų tvarkytuvės veiksmo pasiūlymą, rodomą ekrano apačioje"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Naudoti išsaugotą „passkey“ programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Naudoti išsaugotą prisijungimo informaciją programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pasirinkite išsaugotą prisijungimo informaciją programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
diff --git a/packages/CredentialManager/res/values-mk/strings.xml b/packages/CredentialManager/res/values-mk/strings.xml
index 063c4977ff1b..1fc19f95e6ef 100644
--- a/packages/CredentialManager/res/values-mk/strings.xml
+++ b/packages/CredentialManager/res/values-mk/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Други управници со лозинки"</string>
<string name="close_sheet" msgid="1393792015338908262">"Затворете го листот"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Врати се на претходната страница"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Затворете го предложеното дејство за „Управникот со акредитиви“ што се појавува најдолу на екранот"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Да се користи вашиот зачуван криптографски клуч за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Да се користи вашето зачувано најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Изберете зачувано најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-mn/strings.xml b/packages/CredentialManager/res/values-mn/strings.xml
index 96f33845a3aa..9ae8f93d2da9 100644
--- a/packages/CredentialManager/res/values-mn/strings.xml
+++ b/packages/CredentialManager/res/values-mn/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Нууц үгний бусад менежер"</string>
<string name="close_sheet" msgid="1393792015338908262">"Хүснэгтийг хаах"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Өмнөх хуудас руу буцах"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Дэлгэцийн доод талд гарч ирэх Мандат үнэмлэхийн менежерийн үйлдлийн зөвлөмжийг хаана"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д өөрийн хадгалсан passkey-г ашиглах уу?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д хадгалсан нэвтрэх мэдээллээ ашиглах уу?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д зориулж хадгалсан нэвтрэх мэдээллийг сонгоно уу"</string>
diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml
index 4ebbf9fd66c0..3cff99899bde 100644
--- a/packages/CredentialManager/res/values-mr/strings.xml
+++ b/packages/CredentialManager/res/values-mr/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"इतर पासवर्ड व्यवस्थापक"</string>
<string name="close_sheet" msgid="1393792015338908262">"शीट बंद करा"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"मागील पेजवर परत जा"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"स्क्रीनच्या तळाशी दिसणाऱ्या क्रेडेंशियल व्यवस्थापक कृती सूचना बंद करा"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी तुमची सेव्ह केलेली पासकी वापरायची का?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी तुमचे सेव्ह केलेले साइन-इन वापरायचे का?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सेव्ह केलेले साइन-इन निवडा"</string>
diff --git a/packages/CredentialManager/res/values-ms/strings.xml b/packages/CredentialManager/res/values-ms/strings.xml
index 205a6771d63b..2a1fbed29d96 100644
--- a/packages/CredentialManager/res/values-ms/strings.xml
+++ b/packages/CredentialManager/res/values-ms/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Password Manager lain"</string>
<string name="close_sheet" msgid="1393792015338908262">"Tutup helaian"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kembali ke halaman sebelumnya"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Tutup cadangan tindakan Pengurus Bukti Kelayakan yang muncul di bahagian bawah skrin"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gunakan kunci laluan anda yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gunakan maklumat log masuk anda yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pilih log masuk yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml
index 5547211dab27..70ad36a839c0 100644
--- a/packages/CredentialManager/res/values-my/strings.xml
+++ b/packages/CredentialManager/res/values-my/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"အခြားစကားဝှက်မန်နေဂျာများ"</string>
<string name="close_sheet" msgid="1393792015338908262">"စာမျက်နှာ ပိတ်ရန်"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ယခင်စာမျက်နှာကို ပြန်သွားပါ"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"ဖန်သားပြင်အောက်ခြေတွင် ပြထားသော ‘အထောက်အထားမန်နေဂျာ’ လုပ်ဆောင်မှု အကြံပြုချက်ကို ပိတ်နိုင်သည်"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသောလျှို့ဝှက်ကီး သုံးမလား။"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသောလက်မှတ်ထိုးဝင်မှု သုံးမလား။"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသော လက်မှတ်ထိုးဝင်မှုကို ရွေးပါ"</string>
diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml
index 3540874e59ae..1d8285819656 100644
--- a/packages/CredentialManager/res/values-nb/strings.xml
+++ b/packages/CredentialManager/res/values-nb/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Andre løsninger for passordlagring"</string>
<string name="close_sheet" msgid="1393792015338908262">"Lukk arket"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tilbake til den forrige siden"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Lukk handlingsforslaget fra legitimasjonslagring som vises nedest på skjermen"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vil du bruke den lagrede tilgangsnøkkelen for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vil du bruke den lagrede påloggingen for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Velg en lagret pålogging for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml
index e891d5b1af5d..6f3c3a6154ca 100644
--- a/packages/CredentialManager/res/values-ro/strings.xml
+++ b/packages/CredentialManager/res/values-ro/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Alți manageri de parole"</string>
<string name="close_sheet" msgid="1393792015338908262">"Închide foaia"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Revino la pagina precedentă"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Închide sugestia de acțiune de la Managerul de date de conectare, care apare în partea de jos a ecranului"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Folosești cheia de acces salvată pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Folosești datele de conectare salvate pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Alege o conectare salvată pentru <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-ru/strings.xml b/packages/CredentialManager/res/values-ru/strings.xml
index 6617356c32f6..0a9811b53f15 100644
--- a/packages/CredentialManager/res/values-ru/strings.xml
+++ b/packages/CredentialManager/res/values-ru/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Другие менеджеры паролей"</string>
<string name="close_sheet" msgid="1393792015338908262">"Закрыть лист"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вернуться на предыдущую страницу"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Закрыть подсказку Менеджера учетных данных в нижней части экрана"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Использовать сохраненный ключ доступа для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Использовать сохраненные учетные данные для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Выберите сохраненные данные для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
diff --git a/packages/CredentialManager/res/values-te/strings.xml b/packages/CredentialManager/res/values-te/strings.xml
index 0086710ad40f..8a169532e512 100644
--- a/packages/CredentialManager/res/values-te/strings.xml
+++ b/packages/CredentialManager/res/values-te/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"ఇతర పాస్‌వర్డ్ మేనేజర్‌లు"</string>
<string name="close_sheet" msgid="1393792015338908262">"షీట్‌ను మూసివేయండి"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"మునుపటి పేజీకి తిరిగి వెళ్లండి"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"స్క్రీన్ దిగువున కనిపించే ఆధారాల మేనేజర్ చర్య సూచనను మూసివేయండి"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం మీ సేవ్ చేసిన పాస్-కీ వివరాలను ఉపయోగించాలా?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం మీరు సేవ్ చేసిన సైన్ ఇన్ వివరాలను ఉపయోగించాలా?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం సేవ్ చేసిన సైన్ ఇన్ వివరాలను ఎంచుకోండి"</string>
diff --git a/packages/CredentialManager/res/values-th/strings.xml b/packages/CredentialManager/res/values-th/strings.xml
index 0dfda15c8f94..6bd0eab5d93f 100644
--- a/packages/CredentialManager/res/values-th/strings.xml
+++ b/packages/CredentialManager/res/values-th/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"เครื่องมือจัดการรหัสผ่านอื่นๆ"</string>
<string name="close_sheet" msgid="1393792015338908262">"ปิดชีต"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"กลับไปยังหน้าก่อนหน้า"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"ปิดการแนะนำการดำเนินการของเครื่องมือจัดการข้อมูลเข้าสู่ระบบซึ่งปรากฏที่ด้านล่างของหน้าจอ"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ใช้พาสคีย์ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ใช่ไหม"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ใช้การลงชื่อเข้าใช้ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ใช่ไหม"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"เลือกการลงชื่อเข้าใช้ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
diff --git a/packages/CredentialManager/res/values-tl/strings.xml b/packages/CredentialManager/res/values-tl/strings.xml
index 08d17bc7ef21..3c8cea8c029a 100644
--- a/packages/CredentialManager/res/values-tl/strings.xml
+++ b/packages/CredentialManager/res/values-tl/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Iba pang password manager"</string>
<string name="close_sheet" msgid="1393792015338908262">"Isara ang sheet"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Bumalik sa nakaraang page"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Isara ang suhestyon sa pagkilos ng Manager ng Kredensyal na lumalabas sa ibaba ng screen"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gamitin ang iyong naka-save na passkey para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gamitin ang iyong naka-save na sign-in para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pumili ng naka-save na sign-in para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml
index 7ec70bfb06b1..e6f3de38c0b3 100644
--- a/packages/CredentialManager/res/values-tr/strings.xml
+++ b/packages/CredentialManager/res/values-tr/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"Diğer şifre yöneticileri"</string>
<string name="close_sheet" msgid="1393792015338908262">"Sayfayı kapat"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Önceki sayfaya geri dön"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"Ekranın altında görünen Kimlik Bilgisi Yöneticisi işlem önerisini kapatın"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı şifre anahtarınız kullanılsın mı?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı oturum açma bilgileriniz kullanılsın mı?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı oturum açma bilgilerini kullanın"</string>
diff --git a/packages/CredentialManager/res/values-ur/strings.xml b/packages/CredentialManager/res/values-ur/strings.xml
index 05ab98c147b5..5fbb556d3690 100644
--- a/packages/CredentialManager/res/values-ur/strings.xml
+++ b/packages/CredentialManager/res/values-ur/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"دیگر پاس ورڈ مینیجرز"</string>
<string name="close_sheet" msgid="1393792015338908262">"شیٹ بند کریں"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"گزشتہ صفحے پر واپس جائیں"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"اسکرین کے نیچے ظاہر ہونے والے سند مینیجر کی کاروائی کی تجویز بند کریں"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے اپنی محفوظ کردہ پاس کی استعمال کریں؟"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے اپنے محفوظ کردہ سائن ان کو استعمال کریں؟"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے محفوظ کردہ سائن انز منتخب کریں"</string>
diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml
index 703dfc13f52f..7402cceed7a3 100644
--- a/packages/CredentialManager/res/values-zh-rCN/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"其他密码管理工具"</string>
<string name="close_sheet" msgid="1393792015338908262">"关闭工作表"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一页"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"关闭屏幕底部显示的 Credential Manager 操作建议"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"将您已保存的通行密钥用于<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"将您已保存的登录信息用于<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"为<xliff:g id="APP_NAME">%1$s</xliff:g>选择已保存的登录信息"</string>
diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml
index 31d6993fdc54..e4009b0ca22a 100644
--- a/packages/CredentialManager/res/values-zh-rHK/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"其他密碼管理工具"</string>
<string name="close_sheet" msgid="1393792015338908262">"閂工作表"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一頁"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"關閉顯示在畫面底部的憑證管理工具操作建議"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密鑰嗎?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資料嗎?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"選擇已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資料"</string>
diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml
index 44b921a72371..dcdfeef7e73b 100644
--- a/packages/CredentialManager/res/values-zh-rTW/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml
@@ -40,8 +40,7 @@
<string name="other_password_manager" msgid="565790221427004141">"其他密碼管理工具"</string>
<string name="close_sheet" msgid="1393792015338908262">"關閉功能表"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一頁"</string>
- <!-- no translation found for accessibility_close_button (2953807735590034688) -->
- <skip />
+ <string name="accessibility_close_button" msgid="2953807735590034688">"關閉顯示在畫面底部的憑證管理工具操作建議"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密碼金鑰嗎?"</string>
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資訊嗎?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"選擇已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資訊"</string>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index d2b29245517e..37453c9bc8d4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -22,6 +22,7 @@ import android.app.slice.Slice
import android.app.slice.SliceSpec
import android.content.Context
import android.content.Intent
+import android.content.pm.Signature
import android.credentials.CreateCredentialRequest
import android.credentials.GetCredentialOption
import android.credentials.GetCredentialRequest
@@ -40,6 +41,7 @@ import android.os.Binder
import android.os.Bundle
import android.os.ResultReceiver
import android.service.credentials.CredentialProviderService
+import android.util.ArraySet
import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle
@@ -351,7 +353,8 @@ class CredentialManagerRepo(
intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
or PendingIntent.FLAG_ONE_SHOT))
val createPasswordRequest = android.service.credentials.CreateCredentialRequest(
- context.applicationInfo.packageName,
+ android.service.credentials.CallingAppInfo(
+ context.applicationInfo.packageName, ArraySet<Signature>()),
TYPE_PASSWORD_CREDENTIAL,
toBundle("beckett-bakert@gmail.com", "password123")
)
diff --git a/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml b/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml
index 0e770da7da09..e0c6b2f38133 100644
--- a/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml
@@ -1,17 +1,17 @@
<?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">"Unesite lozinku i nastavite do dinamičnih ažuriranja sistema"</string>
- <string name="notification_install_completed" msgid="6252047868415172643">"Dinamični sistem je spreman. Da biste počeli da ga koristite, restartujte uređaj."</string>
- <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalira se"</string>
- <string name="notification_install_failed" msgid="4066039210317521404">"Instaliranje nije uspelo"</string>
- <string name="notification_image_validation_failed" msgid="2720357826403917016">"Validacija slike diska nije uspela. Otkažite instalaciju."</string>
- <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Trenutno je pokrenut dinamični sistem. Restartujte da biste koristili originalnu verziju Android-a."</string>
- <string name="notification_action_cancel" msgid="5929299408545961077">"Otkaži"</string>
- <string name="notification_action_discard" msgid="1817481003134947493">"Odbaci"</string>
- <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Restartuj"</string>
- <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restartuj"</string>
- <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamični sistem je odbačen"</string>
- <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Restartovanje ili učitavanje dinamičnog sistema nije uspelo"</string>
- <string name="toast_failed_to_disable_dynsystem" msgid="3285742944977744413">"Onemogućavanje dinamičnog sistema nije uspelo"</string>
+ <string name="keyguard_description" msgid="8582605799129954556">"Унесите лозинку и наставите до динамичних ажурирања система"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамични систем је спреман. Да бисте почели да га користите, рестартујте уређај."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Инсталира се"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Инсталирање није успело"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Валидација слике диска није успела. Откажите инсталацију."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Тренутно је покренут динамични систем. Рестартујте да бисте користили оригиналну верзију Android-а."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Откажи"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Одбаци"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Рестартуј"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Рестартуј"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамични систем је одбачен"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Рестартовање или учитавање динамичног система није успело"</string>
+ <string name="toast_failed_to_disable_dynsystem" msgid="3285742944977744413">"Онемогућавање динамичног система није успело"</string>
</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
index ca16c3de21db..68a2d5bbf343 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacite"</string>
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Одбаците"</string>
</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
index 993ec9a92182..9a73269b9d0e 100644
--- a/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/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">"Saznajte više"</string>
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Сазнајте више"</string>
</resources>
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index a53782a0bc6f..36315b58c2a9 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -5,8 +5,7 @@ emilychuang@google.com
evanlaird@google.com
hanxu@google.com
juliacr@google.com
-leifhendrik@google.com
-virgild@google.com
+yantingyang@google.com
ykhung@google.com
# Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-b+sr+Latn/strings.xml
index d51823f3c74e..1478a00e110d 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-b+sr+Latn/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="settings_label" msgid="5948970810295631236">"Podešavanja"</string>
+ <string name="settings_label" msgid="5948970810295631236">"Подешавања"</string>
</resources>
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index dea9bf45d39a..ded62516bb68 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -19,7 +19,7 @@ buildscript {
BUILD_TOOLS_VERSION = "30.0.3"
MIN_SDK = 21
TARGET_SDK = 33
- jetpack_compose_version = '1.4.0-alpha01'
+ jetpack_compose_version = '1.4.0-alpha03'
jetpack_compose_compiler_version = '1.3.2'
}
}
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
index 85ac8ec8e00d..73c109994025 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -75,7 +75,7 @@ dependencies {
api "androidx.slice:slice-builders:1.1.0-alpha02"
api "androidx.slice:slice-core:1.1.0-alpha02"
api "androidx.slice:slice-view:1.1.0-alpha02"
- api "androidx.compose.material3:material3:1.1.0-alpha01"
+ api "androidx.compose.material3:material3:1.1.0-alpha03"
api "androidx.compose.material:material-icons-extended:$jetpack_compose_version"
api "androidx.compose.runtime:runtime-livedata:$jetpack_compose_version"
api "androidx.compose.ui:ui-tooling-preview:$jetpack_compose_version"
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/MessageFormats.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/MessageFormats.kt
index 2adfcca0bf70..edd30dd34860 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/MessageFormats.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/MessageFormats.kt
@@ -25,10 +25,12 @@ import androidx.annotation.StringRes
import java.util.Locale
@RequiresApi(Build.VERSION_CODES.N)
+@SafeVarargs
fun Context.formatString(@StringRes resId: Int, vararg arguments: Pair<String, Any>): String =
resources.formatString(resId, *arguments)
@RequiresApi(Build.VERSION_CODES.N)
+@SafeVarargs
fun Resources.formatString(@StringRes resId: Int, vararg arguments: Pair<String, Any>): String =
MessageFormat(getString(resId), Locale.getDefault(Locale.Category.FORMAT))
.format(mapOf(*arguments))
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerTest.kt
index 0c745d5d5b3d..40e9091fdfaf 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerTest.kt
@@ -41,7 +41,7 @@ class SettingsPagerTest {
composeTestRule.onNodeWithText("Personal").assertIsSelected()
composeTestRule.onNodeWithText("Page 0").assertIsDisplayed()
composeTestRule.onNodeWithText("Work").assertIsNotSelected()
- composeTestRule.onNodeWithText("Page 1").assertIsNotDisplayed()
+ composeTestRule.onNodeWithText("Page 1").assertDoesNotExist()
}
@Test
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
index 69999089b280..af5c5dcd37d5 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
@@ -4,6 +4,8 @@ import android.content.pm.ApplicationInfo
import android.icu.text.CollationKey
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import com.android.settingslib.spaprivileged.template.app.AppListItem
+import com.android.settingslib.spaprivileged.template.app.AppListItemModel
import kotlinx.coroutines.flow.Flow
data class AppEntry<T : AppRecord>(
@@ -69,4 +71,9 @@ interface AppListModel<T : AppRecord> {
*/
@Composable
fun getSummary(option: Int, record: T): State<String>? = null
+
+ @Composable
+ fun AppListItemModel<T>.AppItem() {
+ AppListItem {}
+ }
}
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 2e0d85302b4c..deec267454d6 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
@@ -60,7 +60,6 @@ data class AppListInput<T : AppRecord>(
val listModel: AppListModel<T>,
val state: AppListState,
val header: @Composable () -> Unit,
- val appItem: @Composable AppListItemModel<T>.() -> Unit,
val bottomPadding: Dp,
)
@@ -80,15 +79,13 @@ internal fun <T : AppRecord> AppListInput<T>.AppListImpl(
) {
LogCompositions(TAG, config.userId.toString())
val appListData = appListDataSupplier()
- AppListWidget(appListData, listModel, header, appItem, bottomPadding)
+ listModel.AppListWidget(appListData, header, bottomPadding)
}
@Composable
-private fun <T : AppRecord> AppListWidget(
+private fun <T : AppRecord> AppListModel<T>.AppListWidget(
appListData: State<AppListData<T>?>,
- listModel: AppListModel<T>,
header: @Composable () -> Unit,
- appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
bottomPadding: Dp,
) {
val timeMeasurer = rememberTimeMeasurer(TAG)
@@ -108,14 +105,14 @@ private fun <T : AppRecord> AppListWidget(
}
items(count = list.size, key = { option to list[it].record.app.packageName }) {
- remember(list) { listModel.getGroupTitleIfFirst(option, list, it) }
+ remember(list) { getGroupTitleIfFirst(option, list, it) }
?.let { group -> CategoryTitle(title = group) }
val appEntry = list[it]
- val summary = listModel.getSummary(option, appEntry.record) ?: "".toState()
- appItem(remember(appEntry) {
+ val summary = getSummary(option, appEntry.record) ?: "".toState()
+ remember(appEntry) {
AppListItemModel(appEntry.record, appEntry.label, summary)
- })
+ }.AppItem()
}
}
}
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 cb35fb0b27c7..318bcd93d39d 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
@@ -48,7 +48,6 @@ fun <T : AppRecord> AppListPage(
moreOptions: @Composable MoreOptionsScope.() -> Unit = {},
header: @Composable () -> Unit = {},
appList: @Composable AppListInput<T>.() -> Unit = { AppList() },
- appItem: @Composable AppListItemModel<T>.() -> Unit,
) {
val showSystem = rememberSaveable { mutableStateOf(false) }
SearchScaffold(
@@ -77,7 +76,6 @@ fun <T : AppRecord> AppListPage(
searchQuery = searchQuery,
),
header = header,
- appItem = appItem,
bottomPadding = bottomPadding,
)
appList(appListInput)
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 62db7bd02f7b..76cff0bba875 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
@@ -85,27 +85,6 @@ internal class TogglePermissionAppInfoPageProvider(
fun navigator(permissionType: String, app: ApplicationInfo) =
navigator(route = "$PAGE_NAME/$permissionType/${app.toRoute()}")
- @Composable
- fun <T : AppRecord> EntryItem(
- permissionType: String,
- app: ApplicationInfo,
- listModel: TogglePermissionAppListModel<T>,
- ) {
- val context = LocalContext.current
- val internalListModel = remember {
- TogglePermissionInternalAppListModel(context, listModel, ::RestrictionsProviderImpl)
- }
- 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(
name = PAGE_NAME,
@@ -116,6 +95,32 @@ internal class TogglePermissionAppInfoPageProvider(
}
}
+@Composable
+internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionAppInfoPageEntryItem(
+ permissionType: String,
+ app: ApplicationInfo,
+) {
+ val record = remember { transformItem(app) }
+ if (!remember { isChangeable(record) }) return
+ val context = LocalContext.current
+ val internalListModel = remember {
+ TogglePermissionInternalAppListModel(
+ context = context,
+ permissionType = permissionType,
+ listModel = this,
+ restrictionsProviderFactory = ::RestrictionsProviderImpl,
+ )
+ }
+ Preference(
+ object : PreferenceModel {
+ override val title = stringResource(pageTitleResId)
+ override val summary = internalListModel.getSummary(record)
+ override val onClick =
+ TogglePermissionAppInfoPageProvider.navigator(permissionType, app)
+ }
+ )
+}
+
@VisibleForTesting
@Composable
internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfoPage(
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 5d49d24ecf8e..ce8fc9df7c38 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
@@ -96,7 +96,7 @@ interface TogglePermissionAppListProvider {
@Composable
fun InfoPageEntryItem(app: ApplicationInfo) {
val listModel = rememberContext(::createModel)
- TogglePermissionAppInfoPageProvider.EntryItem(permissionType, app, listModel)
+ listModel.TogglePermissionAppInfoPageEntryItem(permissionType, app)
}
}
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 f65e31040ff6..cbc4822f2896 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
@@ -117,25 +117,23 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp
appList: @Composable AppListInput<T>.() -> Unit = { AppList() },
) {
val context = LocalContext.current
- val internalListModel = remember {
- TogglePermissionInternalAppListModel(context, this, restrictionsProviderFactory)
- }
AppListPage(
title = stringResource(pageTitleResId),
- listModel = internalListModel,
- appList = appList,
- ) {
- AppListItem(
- onClick = TogglePermissionAppInfoPageProvider.navigator(
+ listModel = remember {
+ TogglePermissionInternalAppListModel(
+ context = context,
permissionType = permissionType,
- app = record.app,
- ),
- )
- }
+ listModel = this,
+ restrictionsProviderFactory = restrictionsProviderFactory,
+ )
+ },
+ appList = appList,
+ )
}
internal class TogglePermissionInternalAppListModel<T : AppRecord>(
private val context: Context,
+ private val permissionType: String,
private val listModel: TogglePermissionAppListModel<T>,
private val restrictionsProviderFactory: RestrictionsProviderFactory,
) : AppListModel<T> {
@@ -178,4 +176,14 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>(
null -> context.getString(R.string.summary_placeholder)
}
}
+
+ @Composable
+ override fun AppListItemModel<T>.AppItem() {
+ AppListItem(
+ onClick = TogglePermissionAppInfoPageProvider.navigator(
+ permissionType = permissionType,
+ app = record.app,
+ ),
+ )
+ }
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt
index f2267f61847c..62413864c7df 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt
@@ -122,7 +122,6 @@ class AppListPageTest {
title = TITLE,
listModel = TestAppListModel(options),
header = header,
- appItem = { AppListItem {} },
appList = { appListState.value = this },
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
index 267766992d2c..0154aa194426 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
@@ -99,7 +99,6 @@ class AppListTest {
searchQuery = "".toState(),
),
header = header,
- appItem = { AppListItem {} },
bottomPadding = 0.dp,
)
appListInput.AppListImpl { stateOf(AppListData(appEntries, option = 0)) }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
index ecad08a0aca2..14cb698f7d04 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
@@ -20,13 +20,18 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
+import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
@@ -55,6 +60,8 @@ class TogglePermissionAppInfoPageTest {
@Mock
private lateinit var packageManagers: IPackageManagers
+ private val fakeNavControllerWrapper = FakeNavControllerWrapper()
+
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
private val appListTemplate =
@@ -78,7 +85,58 @@ class TogglePermissionAppInfoPageTest {
}
@Test
- fun title_isDisplayed() {
+ fun entryItem_whenNotChangeable_notDisplayed() {
+ val listModel = TestTogglePermissionAppListModel(isChangeable = false)
+
+ setEntryItem(listModel)
+
+ composeTestRule.onRoot().assertIsNotDisplayed()
+ }
+
+ @Test
+ fun entryItem_whenChangeable_titleDisplayed() {
+ val listModel = TestTogglePermissionAppListModel(isChangeable = true)
+
+ setEntryItem(listModel)
+
+ composeTestRule.onNodeWithText(context.getString(listModel.pageTitleResId))
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun entryItem_whenAllowed_summaryIsAllowed() {
+ val listModel = TestTogglePermissionAppListModel(isAllowed = true, isChangeable = true)
+
+ setEntryItem(listModel)
+
+ composeTestRule.onNodeWithText(context.getString(R.string.app_permission_summary_allowed))
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun entryItem_whenNotAllowed_summaryIsNotAllowed() {
+ val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = true)
+
+ setEntryItem(listModel)
+
+ composeTestRule.onNodeWithText(
+ context.getString(R.string.app_permission_summary_not_allowed)
+ ).assertIsDisplayed()
+ }
+
+ @Test
+ fun entryItem_onClick() {
+ val listModel = TestTogglePermissionAppListModel(isChangeable = true)
+
+ setEntryItem(listModel)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeNavControllerWrapper.navigateCalledWith)
+ .isEqualTo("TogglePermissionAppInfoPage/test.PERMISSION/package.name/0")
+ }
+
+ @Test
+ fun infoPage_title_isDisplayed() {
val listModel = TestTogglePermissionAppListModel()
setTogglePermissionAppInfoPage(listModel)
@@ -88,7 +146,7 @@ class TogglePermissionAppInfoPageTest {
}
@Test
- fun whenAllowed_switchIsOn() {
+ fun infoPage_whenAllowed_switchIsOn() {
val listModel = TestTogglePermissionAppListModel(isAllowed = true)
setTogglePermissionAppInfoPage(listModel)
@@ -98,7 +156,7 @@ class TogglePermissionAppInfoPageTest {
}
@Test
- fun whenNotAllowed_switchIsOff() {
+ fun infoPage_whenNotAllowed_switchIsOff() {
val listModel = TestTogglePermissionAppListModel(isAllowed = false)
setTogglePermissionAppInfoPage(listModel)
@@ -108,7 +166,31 @@ class TogglePermissionAppInfoPageTest {
}
@Test
- fun whenNotChangeable_switchNotEnabled() {
+ fun infoPage_whenChangeableAndClick() {
+ val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = true)
+
+ setTogglePermissionAppInfoPage(listModel)
+ composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
+ .performClick()
+
+ composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
+ .assertIsOn()
+ }
+
+ @Test
+ fun infoPage_whenNotChangeableAndClick() {
+ val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = false)
+
+ setTogglePermissionAppInfoPage(listModel)
+ composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
+ .performClick()
+
+ composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
+ .assertIsOff()
+ }
+
+ @Test
+ fun infoPage_whenNotChangeable_switchNotEnabled() {
val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = false)
setTogglePermissionAppInfoPage(listModel)
@@ -119,7 +201,7 @@ class TogglePermissionAppInfoPageTest {
}
@Test
- fun footer_isDisplayed() {
+ fun infoPage_footer_isDisplayed() {
val listModel = TestTogglePermissionAppListModel()
setTogglePermissionAppInfoPage(listModel)
@@ -128,6 +210,17 @@ class TogglePermissionAppInfoPageTest {
.assertIsDisplayed()
}
+ private fun setEntryItem(listModel: TestTogglePermissionAppListModel) {
+ composeTestRule.setContent {
+ fakeNavControllerWrapper.Wrapper {
+ listModel.TogglePermissionAppInfoPageEntryItem(
+ permissionType = PERMISSION_TYPE,
+ app = APP,
+ )
+ }
+ }
+ }
+
private fun setTogglePermissionAppInfoPage(listModel: TestTogglePermissionAppListModel) {
composeTestRule.setContent {
listModel.TogglePermissionAppInfoPage(
@@ -140,6 +233,7 @@ class TogglePermissionAppInfoPageTest {
}
private companion object {
+ const val PERMISSION_TYPE = "test.PERMISSION"
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
val APP = ApplicationInfo().apply {
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
index 75b884cbad44..961ec10cccd9 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
@@ -51,50 +51,49 @@ class TogglePermissionAppListPageTest {
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
@Test
- fun internalAppListModel_whenAllowed() {
+ fun pageTitle() {
+ val listModel = TestTogglePermissionAppListModel()
+
+ composeTestRule.setContent {
+ listModel.TogglePermissionAppList(
+ permissionType = PERMISSION_TYPE,
+ restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
+ appList = {},
+ )
+ }
+
+ composeTestRule.onNodeWithText(context.getString(listModel.pageTitleResId))
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun summary_whenAllowed() {
fakeRestrictionsProvider.restrictedMode = NoRestricted
val listModel = TestTogglePermissionAppListModel(isAllowed = true)
- val internalAppListModel = TogglePermissionInternalAppListModel(
- context = context,
- listModel = listModel,
- restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
- )
- val summaryState = getSummary(internalAppListModel)
+ val summaryState = getSummary(listModel)
- assertThat(summaryState.value).isEqualTo(
- context.getString(R.string.app_permission_summary_allowed)
- )
+ assertThat(summaryState.value)
+ .isEqualTo(context.getString(R.string.app_permission_summary_allowed))
}
@Test
- fun internalAppListModel_whenNotAllowed() {
+ fun summary_whenNotAllowed() {
fakeRestrictionsProvider.restrictedMode = NoRestricted
val listModel = TestTogglePermissionAppListModel(isAllowed = false)
- val internalAppListModel = TogglePermissionInternalAppListModel(
- context = context,
- listModel = listModel,
- restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
- )
- val summaryState = getSummary(internalAppListModel)
+ val summaryState = getSummary(listModel)
- assertThat(summaryState.value).isEqualTo(
- context.getString(R.string.app_permission_summary_not_allowed)
- )
+ assertThat(summaryState.value)
+ .isEqualTo(context.getString(R.string.app_permission_summary_not_allowed))
}
@Test
- fun internalAppListModel_whenComputingAllowed() {
+ fun summary_whenComputingAllowed() {
fakeRestrictionsProvider.restrictedMode = NoRestricted
val listModel = TestTogglePermissionAppListModel(isAllowed = null)
- val internalAppListModel = TogglePermissionInternalAppListModel(
- context = context,
- listModel = listModel,
- restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
- )
- val summaryState = getSummary(internalAppListModel)
+ val summaryState = getSummary(listModel)
assertThat(summaryState.value).isEqualTo(
context.getString(R.string.summary_placeholder)
@@ -105,16 +104,13 @@ class TogglePermissionAppListPageTest {
fun appListItem_onClick_navigate() {
val listModel = TestTogglePermissionAppListModel()
composeTestRule.setContent {
- listModel.TogglePermissionAppList(
- permissionType = PERMISSION_TYPE,
- restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
- ) {
- fakeNavControllerWrapper.Wrapper {
+ fakeNavControllerWrapper.Wrapper {
+ with(createInternalAppListModel(listModel)) {
AppListItemModel(
record = listModel.transformItem(APP),
label = LABEL,
summary = stateOf(SUMMARY),
- ).appItem()
+ ).AppItem()
}
}
}
@@ -149,12 +145,18 @@ class TogglePermissionAppListPageTest {
.assertIsDisplayed()
}
- private fun getSummary(
- internalAppListModel: TogglePermissionInternalAppListModel<TestAppRecord>,
- ): State<String> {
+ private fun createInternalAppListModel(listModel: TestTogglePermissionAppListModel) =
+ TogglePermissionInternalAppListModel(
+ context = context,
+ permissionType = PERMISSION_TYPE,
+ listModel = listModel,
+ restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
+ )
+
+ private fun getSummary(listModel: TestTogglePermissionAppListModel): State<String> {
lateinit var summary: State<String>
composeTestRule.setContent {
- summary = internalAppListModel.getSummary(record = TestAppRecord(APP))
+ summary = createInternalAppListModel(listModel).getSummary(record = TestAppRecord(APP))
}
return summary
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt
index b13fbb3e8e41..1bfc7ed637af 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt
@@ -18,28 +18,32 @@ package com.android.settingslib.spaprivileged.tests.testutils
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
-import com.android.settingslib.spa.framework.compose.stateOf
-import com.android.settingslib.spaprivileged.test.R
+import androidx.compose.runtime.mutableStateOf
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
+import com.android.settingslib.spaprivileged.test.R
import kotlinx.coroutines.flow.Flow
class TestTogglePermissionAppListModel(
- private val isAllowed: Boolean? = null,
+ isAllowed: Boolean? = null,
private val isChangeable: Boolean = false,
) : TogglePermissionAppListModel<TestAppRecord> {
override val pageTitleResId = R.string.test_permission_title
override val switchTitleResId = R.string.test_permission_switch_title
override val footerResId = R.string.test_permission_footer
+ private val isAllowedState = mutableStateOf(isAllowed)
+
override fun transformItem(app: ApplicationInfo) = TestAppRecord(app = app)
override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<TestAppRecord>>) =
recordListFlow
@Composable
- override fun isAllowed(record: TestAppRecord) = stateOf(isAllowed)
+ override fun isAllowed(record: TestAppRecord) = isAllowedState
override fun isChangeable(record: TestAppRecord) = isChangeable
- override fun setAllowed(record: TestAppRecord, newAllowed: Boolean) {}
+ override fun setAllowed(record: TestAppRecord, newAllowed: Boolean) {
+ isAllowedState.value = newAllowed
+ }
}
diff --git a/packages/SettingsLib/res/drawable/ic_bt_le_audio.xml b/packages/SettingsLib/res/drawable/ic_bt_le_audio.xml
index 5b52a04e1cf6..6afeaf81551d 100644
--- a/packages/SettingsLib/res/drawable/ic_bt_le_audio.xml
+++ b/packages/SettingsLib/res/drawable/ic_bt_le_audio.xml
@@ -13,6 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+
+<!-- The LE audio icon is like headphones and it is the same as ic_bt_headset_hfp.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
@@ -20,12 +22,8 @@
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal" >
<path
- android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
- android:fillColor="#FFFFFFFF"/>
- <path
- android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
- android:fillColor="#FFFFFFFF"/>
- <path
- android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
- android:fillColor="#FFFFFFFF"/>
+ android:fillColor="#FF000000"
+ android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87
+ 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h4v1h-7v2h6c1.66,0 3,-1.34 3,-3V10c0,-4.97 -4.03,-9
+ -9,-9z"/>
</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_bt_le_audio_speakers.xml b/packages/SettingsLib/res/drawable/ic_bt_le_audio_speakers.xml
new file mode 100644
index 000000000000..ac3053a556b2
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_le_audio_speakers.xml
@@ -0,0 +1,31 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal" >
+ <path
+ android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
+ android:fillColor="#FFFFFFFF"/>
+ <path
+ android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="#FFFFFFFF"/>
+ <path
+ android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 3828459ebf67..82e458591ed1 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -467,8 +467,7 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"እስኪሞላ ድረስ <xliff:g id="TIME">%1$s</xliff:g> ይቀራል"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
- <!-- no translation found for power_charging_limited (6732738149313642521) -->
- <skip />
+ <string name="power_charging_limited" msgid="6732738149313642521">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል መሙላት ባለበት ቆሟል"</string>
<string name="power_charging_future_paused" msgid="6829683663982987290">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስከ <xliff:g id="DOCK_DEFENDER_THRESHOLD">%2$s</xliff:g> ድረስ ኃይል መሙላት"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b6939962676d..e675db42787e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1104,9 +1104,9 @@
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> left until full</string>
<!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited -->
- <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging paused</string>
+ <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string>
<!-- [CHAR_LIMIT=80] Label for battery charging future pause -->
- <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging to <xliff:g id="dock_defender_threshold">%2$s</xliff:g></string>
+ <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
index 9dfc8eaac024..c0117b952beb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
@@ -22,13 +22,16 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.LruCache;
+import androidx.annotation.VisibleForTesting;
+
/**
* Cache app icon for management.
*/
public class AppIconCacheManager {
private static final String TAG = "AppIconCacheManager";
private static final float CACHE_RATIO = 0.1f;
- private static final int MAX_CACHE_SIZE_IN_KB = getMaxCacheInKb();
+ @VisibleForTesting
+ static final int MAX_CACHE_SIZE_IN_KB = getMaxCacheInKb();
private static final String DELIMITER = ":";
private static AppIconCacheManager sAppIconCacheManager;
private final LruCache<String, Drawable> mDrawableCache;
@@ -109,4 +112,25 @@ public class AppIconCacheManager {
private static int getMaxCacheInKb() {
return Math.round(CACHE_RATIO * Runtime.getRuntime().maxMemory() / 1024);
}
+
+ /**
+ * Clears as much memory as possible.
+ *
+ * @see android.content.ComponentCallbacks2#onTrimMemory(int)
+ */
+ public void trimMemory(int level) {
+ if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
+ // Time to clear everything
+ if (sAppIconCacheManager != null) {
+ sAppIconCacheManager.mDrawableCache.trimToSize(0);
+ }
+ } else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+ || level == android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
+ // Tough time but still affordable, clear half of the cache
+ if (sAppIconCacheManager != null) {
+ final int maxSize = sAppIconCacheManager.mDrawableCache.maxSize();
+ sAppIconCacheManager.mDrawableCache.trimToSize(maxSize / 2);
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index a9f4e9c74103..4d6dd4b538cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -233,7 +233,22 @@ public class BluetoothEventManager {
@Nullable CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) {
+ Set<CachedBluetoothDevice> memberSet = cachedDevice.getMemberDevice();
boolean isActive = Objects.equals(cachedDevice, activeDevice);
+ if (!isActive && !memberSet.isEmpty()) {
+ for (CachedBluetoothDevice memberCachedDevice : memberSet) {
+ isActive = Objects.equals(memberCachedDevice, activeDevice);
+ if (isActive) {
+ Log.d(TAG,
+ "The active device is the member device "
+ + activeDevice.getDevice().getAnonymizedAddress()
+ + ". change activeDevice as main device "
+ + cachedDevice.getDevice().getAnonymizedAddress());
+ activeDevice = cachedDevice;
+ break;
+ }
+ }
+ }
cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
}
for (BluetoothCallback callback : mCallbacks) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
index 64f8bef1ecf3..1b0e1f1236c8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
@@ -34,11 +34,21 @@ import org.robolectric.RobolectricTestRunner;
public class AppIconCacheManagerTest {
private static final String APP_PACKAGE_NAME = "com.test.app";
+ private static final String APP_PACKAGE_NAME1 = "com.test.app1";
+ private static final String APP_PACKAGE_NAME2 = "com.test.app2";
+ private static final String APP_PACKAGE_NAME3 = "com.test.app3";
private static final int APP_UID = 9999;
@Mock
private Drawable mIcon;
+ @Mock
+ private Drawable mIcon1;
+ @Mock
+ private Drawable mIcon2;
+ @Mock
+ private Drawable mIcon3;
+
private AppIconCacheManager mAppIconCacheManager;
@Before
@@ -48,6 +58,29 @@ public class AppIconCacheManagerTest {
doReturn(10).when(mIcon).getIntrinsicHeight();
doReturn(10).when(mIcon).getIntrinsicWidth();
doReturn(mIcon).when(mIcon).mutate();
+
+ // Algorithm for trim memory test:
+ // The real maxsize is defined by AppIconCacheManager.MAX_CACHE_SIZE_IN_KB, and the size
+ // of each element is calculated as following:
+ // n * n * 4 / 1024
+ // In the testcase, we want to mock the maxsize of LruCache is 3, so the formula calculating
+ // the size of each element will be like:
+ // n * n * 4 / 1024 = maxsize / 3
+ // Thus, n = square_root(maxsize / 3 * 1024 / 4), which can be used as an icon size.
+ final int iconSize =
+ (int) Math.sqrt(AppIconCacheManager.MAX_CACHE_SIZE_IN_KB / 3f * 1024f / 4f);
+
+ doReturn(iconSize).when(mIcon1).getIntrinsicHeight();
+ doReturn(iconSize).when(mIcon1).getIntrinsicWidth();
+ doReturn(mIcon1).when(mIcon1).mutate();
+
+ doReturn(iconSize).when(mIcon2).getIntrinsicHeight();
+ doReturn(iconSize).when(mIcon2).getIntrinsicWidth();
+ doReturn(mIcon2).when(mIcon2).mutate();
+
+ doReturn(iconSize).when(mIcon3).getIntrinsicHeight();
+ doReturn(iconSize).when(mIcon3).getIntrinsicWidth();
+ doReturn(mIcon3).when(mIcon3).mutate();
}
@After
@@ -106,4 +139,41 @@ public class AppIconCacheManagerTest {
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
}
+
+ @Test
+ public void trimMemory_levelSatisfied_shouldNotCacheIcon() {
+
+ mAppIconCacheManager.put(APP_PACKAGE_NAME1, APP_UID, mIcon1);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME2, APP_UID, mIcon2);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME3, APP_UID, mIcon3);
+
+ // Expected to trim size to 0
+ final int level = android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+ mAppIconCacheManager.trimMemory(level);
+
+ // None of the elements should be cached
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID)).isNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME2, APP_UID)).isNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME3, APP_UID)).isNull();
+ }
+
+ @Test
+ public void trimMemory_levelSatisfied_shouldCacheAtLeastHalf() {
+
+ mAppIconCacheManager.put(APP_PACKAGE_NAME1, APP_UID, mIcon1);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME2, APP_UID, mIcon2);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME3, APP_UID, mIcon3);
+
+ // Get the last element
+ mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID);
+
+ // Expected to trim size to half of it, which is int( 3 / 2 ) = 1
+ final int level = android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
+ mAppIconCacheManager.trimMemory(level);
+
+ // There should be only one cached element, which is the last recently used one
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID)).isNotNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME2, APP_UID)).isNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME3, APP_UID)).isNull();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 7b94492cef4f..3361a66e958d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -70,10 +70,14 @@ public class BluetoothEventManagerTest {
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
+ private LeAudioProfile mLeAudioProfile;
+ @Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
@Mock
+ private BluetoothDevice mDevice3;
+ @Mock
private LocalBluetoothProfileManager mLocalProfileManager;
@Mock
private BluetoothUtils.ErrorListener mErrorListener;
@@ -83,6 +87,7 @@ public class BluetoothEventManagerTest {
private BluetoothEventManager mBluetoothEventManager;
private CachedBluetoothDevice mCachedDevice1;
private CachedBluetoothDevice mCachedDevice2;
+ private CachedBluetoothDevice mCachedDevice3;
@Before
public void setUp() {
@@ -95,9 +100,10 @@ public class BluetoothEventManagerTest {
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
-
+ when(mLeAudioProfile.isProfileReady()).thenReturn(true);
mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1);
mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2);
+ mCachedDevice3 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3);
BluetoothUtils.setErrorListener(mErrorListener);
}
@@ -293,6 +299,43 @@ public class BluetoothEventManagerTest {
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
}
+ @Test
+ public void dispatchActiveDeviceChanged_connectedMemberDevices_activeDeviceChanged() {
+ final List<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
+ cachedDevices.add(mCachedDevice1);
+ cachedDevices.add(mCachedDevice2);
+
+ int group1 = 1;
+ when(mDevice3.getAddress()).thenReturn("testAddress3");
+ mCachedDevice1.setGroupId(group1);
+ mCachedDevice3.setGroupId(group1);
+ mCachedDevice1.addMemberDevice(mCachedDevice3);
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice3.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
+
+ // Connect device1 and device3 for LE and device2 for A2DP and HFP
+ mCachedDevice1.onProfileStateChanged(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice3.onProfileStateChanged(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice2.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice2.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+
+ // Verify that both devices are connected and none is Active
+ assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).isFalse();
+ assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(mCachedDevice3.isActiveDevice(BluetoothProfile.LE_AUDIO)).isFalse();
+
+ // The member device is active.
+ mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice3,
+ BluetoothProfile.LE_AUDIO);
+
+ // The main device is active since the member is active.
+ assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).isTrue();
+ }
+
/**
* Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid.
*/
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 682774e76505..c7db275a80e5 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -787,6 +787,9 @@
<!-- Permission required for CTS test - ApplicationExemptionsTests -->
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS" />
+ <!-- Permission required for CTS test - CtsPackageInstallTestCases-->
+ <uses-permission android:name="android.permission.GET_APP_METADATA" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/res/values-b+sr+Latn/strings.xml b/packages/Shell/res/values-b+sr+Latn/strings.xml
index 805aed68ba81..3513afcec2de 100644
--- a/packages/Shell/res/values-b+sr+Latn/strings.xml
+++ b/packages/Shell/res/values-b+sr+Latn/strings.xml
@@ -17,31 +17,31 @@
<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="3701846017049540910">"Shell"</string>
- <string name="bugreport_notification_channel" msgid="2574150205913861141">"Izveštaji o greškama"</string>
- <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Izveštaj o grešci <xliff:g id="ID">#%d</xliff:g> se generiše"</string>
- <string name="bugreport_finished_title" msgid="4429132808670114081">"Izveštaj o grešci <xliff:g id="ID">#%d</xliff:g> je snimljen"</string>
- <string name="bugreport_updating_title" msgid="4423539949559634214">"Dodaju se detalji u izveštaj o grešci"</string>
- <string name="bugreport_updating_wait" msgid="3322151947853929470">"Sačekajte..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Izveštaj o grešci će se uskoro pojaviti na telefonu"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Izaberite da biste delili izveštaj o grešci"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Dodirnite da biste delili izveštaj o grešci"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Izaberite da biste delili izveštaj o grešci bez snimka ekrana ili sačekajte da se napravi snimak ekrana"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Dodirnite za deljenje izveštaja o grešci bez snimka ekrana ili sačekajte da se napravi snimak ekrana"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Dodirnite za deljenje izveštaja o grešci bez snimka ekrana ili sačekajte da se napravi snimak ekrana"</string>
- <string name="bugreport_confirm" msgid="5917407234515812495">"Izveštaji o greškama sadrže podatke iz različitih sistemskih datoteka evidencije, koji obuhvataju lične i privatne podatke (poput korišćenja aplikacija i podataka o lokaciji). Delite izveštaje o greškama samo sa aplikacijama i ljudima u koje imate poverenja."</string>
- <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Ne prikazuj ponovo"</string>
- <string name="bugreport_storage_title" msgid="5332488144740527109">"Izveštaji o greškama"</string>
- <string name="bugreport_unreadable_text" msgid="586517851044535486">"Datoteka izveštaja o grešci ne može da se pročita"</string>
- <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Dodavanje detalja izveštaja o grešci u zip datoteku nije uspelo"</string>
- <string name="bugreport_unnamed" msgid="2800582406842092709">"neimenovano"</string>
- <string name="bugreport_info_action" msgid="2158204228510576227">"Detalji"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Snimci ekrana"</string>
- <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Snimak ekrana je napravljen."</string>
- <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Snimanje ekrana nije uspelo."</string>
- <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Detalji izveštaja o grešci <xliff:g id="ID">#%d</xliff:g>"</string>
- <string name="bugreport_info_name" msgid="4414036021935139527">"Naziv datoteke"</string>
- <string name="bugreport_info_title" msgid="2306030793918239804">"Naslov greške"</string>
- <string name="bugreport_info_description" msgid="5072835127481627722">"Rezime greške"</string>
- <string name="save" msgid="4781509040564835759">"Sačuvaj"</string>
- <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Deli izveštaj o grešci"</string>
+ <string name="bugreport_notification_channel" msgid="2574150205913861141">"Извештаји о грешкама"</string>
+ <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Извештај о грешци <xliff:g id="ID">#%d</xliff:g> се генерише"</string>
+ <string name="bugreport_finished_title" msgid="4429132808670114081">"Извештај о грешци <xliff:g id="ID">#%d</xliff:g> је снимљен"</string>
+ <string name="bugreport_updating_title" msgid="4423539949559634214">"Додају се детаљи у извештај о грешци"</string>
+ <string name="bugreport_updating_wait" msgid="3322151947853929470">"Сачекајте..."</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Извештај о грешци ће се ускоро појавити на телефону"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Изаберите да бисте делили извештај о грешци"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Додирните да бисте делили извештај о грешци"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Изаберите да бисте делили извештај о грешци без снимка екрана или сачекајте да се направи снимак екрана"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Додирните за дељење извештаја о грешци без снимка екрана или сачекајте да се направи снимак екрана"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Додирните за дељење извештаја о грешци без снимка екрана или сачекајте да се направи снимак екрана"</string>
+ <string name="bugreport_confirm" msgid="5917407234515812495">"Извештаји о грешкама садрже податке из различитих системских датотека евиденције, који обухватају личне и приватне податке (попут коришћења апликацијa и података о локацији). Делите извештаје о грешкама само са апликацијама и људима у које имате поверења."</string>
+ <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Не приказуј поново"</string>
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаји о грешкама"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Датотека извештаја о грешци не може да се прочита"</string>
+ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Додавање детаља извештаја о грешци у zip датотеку није успело"</string>
+ <string name="bugreport_unnamed" msgid="2800582406842092709">"неименовано"</string>
+ <string name="bugreport_info_action" msgid="2158204228510576227">"Детаљи"</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Снимци екрана"</string>
+ <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Снимак екрана је направљен."</string>
+ <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Снимање екрана није успело."</string>
+ <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Детаљи извештаја о грешци <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_info_name" msgid="4414036021935139527">"Назив датотеке"</string>
+ <string name="bugreport_info_title" msgid="2306030793918239804">"Наслов грешке"</string>
+ <string name="bugreport_info_description" msgid="5072835127481627722">"Резиме грешке"</string>
+ <string name="save" msgid="4781509040564835759">"Сачувај"</string>
+ <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Дели извештај о грешци"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-b+sr+Latn/strings.xml b/packages/SoundPicker/res/values-b+sr+Latn/strings.xml
index 947c85c8ad91..bc573f5a2662 100644
--- a/packages/SoundPicker/res/values-b+sr+Latn/strings.xml
+++ b/packages/SoundPicker/res/values-b+sr+Latn/strings.xml
@@ -16,14 +16,14 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="ringtone_default" msgid="798836092118824500">"Podrazumevani zvuk zvona"</string>
- <string name="notification_sound_default" msgid="8133121186242636840">"Podrazumevani zvuk obaveštenja"</string>
- <string name="alarm_sound_default" msgid="4787646764557462649">"Podrazumevani zvuk alarma"</string>
- <string name="add_ringtone_text" msgid="6642389991738337529">"Dodaj melodiju zvona"</string>
- <string name="add_alarm_text" msgid="3545497316166999225">"Dodajte alarm"</string>
- <string name="add_notification_text" msgid="4431129543300614788">"Dodajte obaveštenje"</string>
- <string name="delete_ringtone_text" msgid="201443984070732499">"Izbriši"</string>
- <string name="unable_to_add_ringtone" msgid="4583511263449467326">"Dodavanje prilagođene melodije zvona nije uspelo"</string>
- <string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Brisanje prilagođene melodije zvona nije uspelo"</string>
- <string name="app_label" msgid="3091611356093417332">"Zvukovi"</string>
+ <string name="ringtone_default" msgid="798836092118824500">"Подразумевани звук звона"</string>
+ <string name="notification_sound_default" msgid="8133121186242636840">"Подразумевани звук обавештења"</string>
+ <string name="alarm_sound_default" msgid="4787646764557462649">"Подразумевани звук аларма"</string>
+ <string name="add_ringtone_text" msgid="6642389991738337529">"Додај мелодију звона"</string>
+ <string name="add_alarm_text" msgid="3545497316166999225">"Додајте аларм"</string>
+ <string name="add_notification_text" msgid="4431129543300614788">"Додајте обавештење"</string>
+ <string name="delete_ringtone_text" msgid="201443984070732499">"Избриши"</string>
+ <string name="unable_to_add_ringtone" msgid="4583511263449467326">"Додавање прилагођене мелодије звона није успело"</string>
+ <string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Брисање прилагођене мелодије звона није успело"</string>
+ <string name="app_label" msgid="3091611356093417332">"Звукови"</string>
</resources>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6984b5ab6fb4..53ee4403e8bc 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -896,7 +896,7 @@
android:showForAllUsers="true"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:visibleToInstantApps="true">
</activity>
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt
index cb1a5f958ed6..ec5e70383eb3 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt
@@ -58,7 +58,9 @@ class FakeKeyguardQuickAffordanceProviderClient(
flags: List<KeyguardQuickAffordanceProviderClient.Flag> =
listOf(
KeyguardQuickAffordanceProviderClient.Flag(
- name = KeyguardQuickAffordanceProviderContract.FlagsTable.FLAG_NAME_FEATURE_ENABLED,
+ name =
+ KeyguardQuickAffordanceProviderContract.FlagsTable
+ .FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
value = true,
)
),
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt
index 17be74b08690..923b99f0b750 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt
@@ -148,7 +148,11 @@ object KeyguardQuickAffordanceProviderContract {
/**
* Flag denoting whether the customizable lock screen quick affordances feature is enabled.
*/
- const val FLAG_NAME_FEATURE_ENABLED = "is_feature_enabled"
+ const val FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED =
+ "is_custom_lock_screen_quick_affordances_feature_enabled"
+
+ /** Flag denoting whether the customizable clocks feature is enabled. */
+ const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
object Columns {
/** String. Unique ID for the flag. */
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 0dd4a83fba91..40f2152475f2 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -30,8 +30,7 @@
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ኃይል በመሙላት ላይ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1657547879230699837) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ባትሪን ለመጠበቅ ኃይል መሙላት ባለበት ቆሟል"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ለመክፈት ምናሌ ተጫን።"</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"አውታረ መረብ ተቆልፏል"</string>
<!-- no translation found for keyguard_missing_sim_message_short (685029586173458728) -->
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index 6122907edbdc..2724cef3a777 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -20,19 +20,19 @@
<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">"Unesite PIN"</string>
- <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Unesite šablon"</string>
- <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Unesite lozinku"</string>
- <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Nevažeća kartica."</string>
- <string name="keyguard_charged" msgid="5478247181205188995">"Napunjena je"</string>
- <string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bežično punjenje"</string>
- <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Puni se"</string>
- <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Puni se"</string>
- <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo se puni"</string>
- <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo se puni"</string>
- <string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje je pauzirano da bi se zaštitila baterija"</string>
- <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Meni da biste otključali."</string>
- <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string>
+ <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Унесите PIN"</string>
+ <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Унесите шаблон"</string>
+ <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Унесите лозинку"</string>
+ <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Неважећа картица."</string>
+ <string name="keyguard_charged" msgid="5478247181205188995">"Напуњена је"</string>
+ <string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бежично пуњење"</string>
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуни се"</string>
+ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуни се"</string>
+ <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо се пуни"</string>
+ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Споро се пуни"</string>
+ <string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуњење је паузирано да би се заштитила батерија"</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притисните Мени да бисте откључали."</string>
+ <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежа је закључана"</string>
<!-- no translation found for keyguard_missing_sim_message_short (685029586173458728) -->
<skip />
<!-- no translation found for keyguard_missing_sim_instructions (7735360104844653246) -->
@@ -49,55 +49,55 @@
<skip />
<!-- no translation found for keyguard_sim_unlock_progress_dialog_message (8489092646014631659) -->
<skip />
- <string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"Oblast za PIN"</string>
- <string name="keyguard_accessibility_password" msgid="3524161948484801450">"Lozinka za uređaj"</string>
- <string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"Oblast za PIN za SIM"</string>
- <string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"Oblast za PUK za SIM"</string>
- <string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Izbriši"</string>
- <string name="disable_carrier_button_text" msgid="7153361131709275746">"Onemogući eSIM"</string>
- <string name="error_disable_esim_title" msgid="3802652622784813119">"Onemogućavanje eSIM-a nije uspelo"</string>
- <string name="error_disable_esim_msg" msgid="2441188596467999327">"eSIM ne može da se onemogući zbog greške."</string>
+ <string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"Област за PIN"</string>
+ <string name="keyguard_accessibility_password" msgid="3524161948484801450">"Лозинка за уређај"</string>
+ <string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"Област за PIN за SIM"</string>
+ <string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"Област за PUK за SIM"</string>
+ <string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Избриши"</string>
+ <string name="disable_carrier_button_text" msgid="7153361131709275746">"Онемогући eSIM"</string>
+ <string name="error_disable_esim_title" msgid="3802652622784813119">"Онемогућавање eSIM-а није успело"</string>
+ <string name="error_disable_esim_msg" msgid="2441188596467999327">"eSIM не може да се онемогући због грешке."</string>
<string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Enter"</string>
- <string name="kg_wrong_pattern" msgid="5907301342430102842">"Pogrešan šablon"</string>
- <string name="kg_wrong_password" msgid="4143127991071670512">"Pogrešna lozinka"</string>
- <string name="kg_wrong_pin" msgid="4160978845968732624">"Pogrešan PIN"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Probajte ponovo za # sekundu.}one{Probajte ponovo za # sekundu.}few{Probajte ponovo za # sekunde.}other{Probajte ponovo za # sekundi.}}"</string>
- <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Unesite PIN za SIM."</string>
- <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Unesite PIN za SIM „<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> Onemogućite eSIM da biste uređaj koristili bez mobilne usluge."</string>
- <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"SIM kartica je sada onemogućena. Unesite PUK kôd da biste nastavili. Detaljne informacije potražite od mobilnog operatera."</string>
- <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"SIM „<xliff:g id="CARRIER">%1$s</xliff:g>“ je sada onemogućen. Unesite PUK kôd da biste nastavili. Detaljne informacije potražite od mobilnog operatera."</string>
- <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Unesite željeni PIN kôd"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Potvrdite željeni PIN kôd"</string>
+ <string name="kg_wrong_pattern" msgid="5907301342430102842">"Погрешан шаблон"</string>
+ <string name="kg_wrong_password" msgid="4143127991071670512">"Погрешна лозинка"</string>
+ <string name="kg_wrong_pin" msgid="4160978845968732624">"Погрешан PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Пробајте поново за # секунду.}one{Пробајте поново за # секунду.}few{Пробајте поново за # секунде.}other{Пробајте поново за # секунди.}}"</string>
+ <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Унесите PIN за SIM."</string>
+ <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Унесите PIN за SIM „<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> Онемогућите eSIM да бисте уређај користили без мобилне услуге."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"SIM картица је сада онемогућена. Унесите PUK кôд да бисте наставили. Детаљне информације потражите од мобилног оператера."</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"SIM „<xliff:g id="CARRIER">%1$s</xliff:g>“ је сада онемогућен. Унесите PUK кôд да бисте наставили. Детаљне информације потражите од мобилног оператера."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Унесите жељени PIN кôд"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Потврдите жељени PIN кôд"</string>
<!-- no translation found for kg_sim_unlock_progress_dialog_message (1123048780346295748) -->
<skip />
- <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Unesite PIN koji ima 4–8 brojeva."</string>
- <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK kôd treba da ima 8 ili više brojeva."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Uneli ste pogrešan PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Uneli ste pogrešnu lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Nacrtali ste netačan šablon za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
- <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Netačan PIN kôd za SIM. Sada morate da kontaktirate mobilnog operatera da biste otključali uređaj."</string>
- <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Netačan PIN za SIM kôd. Imate još # pokušaj, a onda morate da se obratite mobilnom operateru da biste otključali uređaj.}one{Netačan PIN za SIM kôd. Imate još # pokušaj. }few{Netačan PIN za SIM kôd. Imate još # pokušaja. }other{Netačan PIN za SIM kôd. Imate još # pokušaja. }}"</string>
- <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM kartica je neupotrebljiva. Kontaktirajte mobilnog operatera."</string>
- <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Netačan SIM PUK kôd. Imate još # pokušaj pre nego što SIM kartica postane trajno neupotrebljiva.}one{Netačan PUK kôd za SIM. Imate još # pokušaj pre nego što SIM kartica postane trajno neupotrebljiva.}few{Netačan PUK kôd za SIM. Imate još # pokušaja pre nego što SIM kartica postane trajno neupotrebljiva.}other{Netačan PUK kôd za SIM. Imate još # pokušaja pre nego što SIM kartica postane trajno neupotrebljiva.}}"</string>
- <string name="kg_password_pin_failed" msgid="5136259126330604009">"Radnja sa PIN kodom za SIM nije uspela!"</string>
- <string name="kg_password_puk_failed" msgid="6778867411556937118">"Radnja sa PUK kodom za SIM nije uspela!"</string>
- <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promeni metod unosa"</string>
- <string name="airplane_mode" msgid="2528005343938497866">"Režim rada u avionu"</string>
- <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Treba da unesete šablon kada se uređaj ponovo pokrene"</string>
- <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Treba da unesete PIN kada se uređaj ponovo pokrene"</string>
- <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Treba da unesete lozinku kada se uređaj ponovo pokrene"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Za dodatnu bezbednost koristite šablon"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Za dodatnu bezbednost koristite PIN"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Za dodatnu bezbednost koristite lozinku"</string>
- <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Administrator je zaključao uređaj"</string>
- <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
- <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznat"</string>
- <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Otključavanje licem traži pristup kameri u Podešavanjima"</string>
- <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Unesite PIN za SIM. Još # pokušaj i moraćete da se obratite mobilnom operateru da biste otključali uređaj.}one{Unesite PIN za SIM. Imate još # pokušaj.}few{Unesite PIN za SIM. Imate još # pokušaja.}other{Unesite PIN za SIM. Imate još # pokušaja.}}"</string>
- <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaj pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.}one{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaj pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.}few{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.}other{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.}}"</string>
- <string name="clock_title_default" msgid="6342735240617459864">"Podrazumevani"</string>
- <string name="clock_title_bubble" msgid="2204559396790593213">"Mehurići"</string>
- <string name="clock_title_analog" msgid="8409262532900918273">"Analogni"</string>
- <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Otključajte uređaj da biste nastavili"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Унесите PIN који има 4–8 бројева."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK кôд треба да има 8 или више бројева."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Унели сте погрешан PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Унели сте погрешну лозинку <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Нацртали сте нетачан шаблон за откључавање <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Нетачан PIN кôд за SIM. Сада морате да контактирате мобилног оператера да бисте откључали уређај."</string>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Нетачан PIN за SIM кôд. Имате још # покушај, а онда морате да се обратите мобилном оператеру да бисте откључали уређај.}one{Нетачан PIN за SIM кôд. Имате још # покушај. }few{Нетачан PIN за SIM кôд. Имате још # покушаја. }other{Нетачан PIN за SIM кôд. Имате још # покушаја. }}"</string>
+ <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM картица је неупотребљива. Контактирајте мобилног оператера."</string>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Нетачан SIM PUK кôд. Имате још # покушај пре него што SIM картица постане трајно неупотребљива.}one{Нетачан PUK кôд за SIM. Имате још # покушај пре него што SIM картица постане трајно неупотребљива.}few{Нетачан PUK кôд за SIM. Имате још # покушаја пре него што SIM картица постане трајно неупотребљива.}other{Нетачан PUK кôд за SIM. Имате још # покушаја пре него што SIM картица постане трајно неупотребљива.}}"</string>
+ <string name="kg_password_pin_failed" msgid="5136259126330604009">"Радња са PIN кодом за SIM није успела!"</string>
+ <string name="kg_password_puk_failed" msgid="6778867411556937118">"Радња са PUK кодом за SIM није успела!"</string>
+ <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Промени метод уноса"</string>
+ <string name="airplane_mode" msgid="2528005343938497866">"Режим рада у авиону"</string>
+ <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Треба да унесете шаблон када се уређај поново покрене"</string>
+ <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Треба да унесете PIN када се уређај поново покрене"</string>
+ <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Треба да унесете лозинку када се уређај поново покрене"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"За додатну безбедност користите шаблон"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"За додатну безбедност користите PIN"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"За додатну безбедност користите лозинку"</string>
+ <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Администратор је закључао уређај"</string>
+ <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Уређај је ручно закључан"</string>
+ <string name="kg_face_not_recognized" msgid="7903950626744419160">"Није препознат"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Откључавање лицем тражи приступ камери у Подешавањима"</string>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Унесите PIN за SIM. Још # покушај и мораћете да се обратите мобилном оператеру да бисте откључали уређај.}one{Унесите PIN за SIM. Имате још # покушај.}few{Унесите PIN за SIM. Имате још # покушаја.}other{Унесите PIN за SIM. Имате још # покушаја.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још # покушај пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.}one{SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још # покушај пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.}few{SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још # покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.}other{SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још # покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.}}"</string>
+ <string name="clock_title_default" msgid="6342735240617459864">"Подразумевани"</string>
+ <string name="clock_title_bubble" msgid="2204559396790593213">"Мехурићи"</string>
+ <string name="clock_title_analog" msgid="8409262532900918273">"Аналогни"</string>
+ <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Откључајте уређај да бисте наставили"</string>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index 80f3fb720791..cdd4dee025f2 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -33,22 +33,14 @@
<string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging paused to protect battery"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
- <!-- no translation found for keyguard_missing_sim_message_short (685029586173458728) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions (7735360104844653246) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions_long (3451467338947610268) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_message_short (3955052454216046100) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_instructions (5034635040020685428) -->
- <skip />
- <!-- no translation found for keyguard_sim_locked_message (7095293254587575270) -->
- <skip />
- <!-- no translation found for keyguard_sim_puk_locked_message (2503428315518592542) -->
- <skip />
- <!-- no translation found for keyguard_sim_unlock_progress_dialog_message (8489092646014631659) -->
- <skip />
+ <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string>
<string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"PIN area"</string>
<string name="keyguard_accessibility_password" msgid="3524161948484801450">"Device password"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"SIM PIN area"</string>
@@ -69,8 +61,7 @@
<string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\" is now disabled. Enter PUK code to continue. Contact operator for details."</string>
<string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Enter desired PIN code"</string>
<string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirm desired PIN code"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (1123048780346295748) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Unlocking SIM…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Type a PIN that is 4 to 8 numbers."</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK code should be 8 numbers or more."</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 794c26086ac9..8222c710c282 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -33,22 +33,14 @@
<string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging paused to protect battery"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
- <!-- no translation found for keyguard_missing_sim_message_short (685029586173458728) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions (7735360104844653246) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions_long (3451467338947610268) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_message_short (3955052454216046100) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_instructions (5034635040020685428) -->
- <skip />
- <!-- no translation found for keyguard_sim_locked_message (7095293254587575270) -->
- <skip />
- <!-- no translation found for keyguard_sim_puk_locked_message (2503428315518592542) -->
- <skip />
- <!-- no translation found for keyguard_sim_unlock_progress_dialog_message (8489092646014631659) -->
- <skip />
+ <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string>
<string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"PIN area"</string>
<string name="keyguard_accessibility_password" msgid="3524161948484801450">"Device password"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"SIM PIN area"</string>
@@ -69,8 +61,7 @@
<string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\" is now disabled. Enter PUK code to continue. Contact carrier for details."</string>
<string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Enter desired PIN code"</string>
<string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirm desired PIN code"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (1123048780346295748) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Unlocking SIM…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Type a PIN that is 4 to 8 numbers."</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK code should be 8 numbers or more."</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index 80f3fb720791..cdd4dee025f2 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -33,22 +33,14 @@
<string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging paused to protect battery"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
- <!-- no translation found for keyguard_missing_sim_message_short (685029586173458728) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions (7735360104844653246) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions_long (3451467338947610268) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_message_short (3955052454216046100) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_instructions (5034635040020685428) -->
- <skip />
- <!-- no translation found for keyguard_sim_locked_message (7095293254587575270) -->
- <skip />
- <!-- no translation found for keyguard_sim_puk_locked_message (2503428315518592542) -->
- <skip />
- <!-- no translation found for keyguard_sim_unlock_progress_dialog_message (8489092646014631659) -->
- <skip />
+ <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string>
<string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"PIN area"</string>
<string name="keyguard_accessibility_password" msgid="3524161948484801450">"Device password"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"SIM PIN area"</string>
@@ -69,8 +61,7 @@
<string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\" is now disabled. Enter PUK code to continue. Contact operator for details."</string>
<string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Enter desired PIN code"</string>
<string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirm desired PIN code"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (1123048780346295748) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Unlocking SIM…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Type a PIN that is 4 to 8 numbers."</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK code should be 8 numbers or more."</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index 80f3fb720791..cdd4dee025f2 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -33,22 +33,14 @@
<string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging paused to protect battery"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
- <!-- no translation found for keyguard_missing_sim_message_short (685029586173458728) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions (7735360104844653246) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions_long (3451467338947610268) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_message_short (3955052454216046100) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_instructions (5034635040020685428) -->
- <skip />
- <!-- no translation found for keyguard_sim_locked_message (7095293254587575270) -->
- <skip />
- <!-- no translation found for keyguard_sim_puk_locked_message (2503428315518592542) -->
- <skip />
- <!-- no translation found for keyguard_sim_unlock_progress_dialog_message (8489092646014631659) -->
- <skip />
+ <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string>
<string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"PIN area"</string>
<string name="keyguard_accessibility_password" msgid="3524161948484801450">"Device password"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"SIM PIN area"</string>
@@ -69,8 +61,7 @@
<string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\" is now disabled. Enter PUK code to continue. Contact operator for details."</string>
<string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Enter desired PIN code"</string>
<string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirm desired PIN code"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (1123048780346295748) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Unlocking SIM…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Type a PIN that is 4 to 8 numbers."</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK code should be 8 numbers or more."</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index 7a271d46bfe5..5302771bf8ff 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -33,22 +33,14 @@
<string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging paused to protect battery‎‏‎‎‏‎"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‎‏‎‎Press Menu to unlock.‎‏‎‎‏‎"</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎Network locked‎‏‎‎‏‎"</string>
- <!-- no translation found for keyguard_missing_sim_message_short (685029586173458728) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions (7735360104844653246) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions_long (3451467338947610268) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_message_short (3955052454216046100) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_instructions (5034635040020685428) -->
- <skip />
- <!-- no translation found for keyguard_sim_locked_message (7095293254587575270) -->
- <skip />
- <!-- no translation found for keyguard_sim_puk_locked_message (2503428315518592542) -->
- <skip />
- <!-- no translation found for keyguard_sim_unlock_progress_dialog_message (8489092646014631659) -->
- <skip />
+ <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎No SIM‎‏‎‎‏‎"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎Add a SIM.‎‏‎‎‏‎"</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‎The SIM is missing or not readable. Add a SIM.‎‏‎‎‏‎"</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎Unusable SIM.‎‏‎‎‏‎"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎Your SIM has been permanently deactivated.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ Contact your wireless service provider for another SIM.‎‏‎‎‏‎"</string>
+ <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‎SIM is locked.‎‏‎‎‏‎"</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎SIM is PUK-locked.‎‏‎‎‏‎"</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎Unlocking SIM…‎‏‎‎‏‎"</string>
<string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎PIN area‎‏‎‎‏‎"</string>
<string name="keyguard_accessibility_password" msgid="3524161948484801450">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎Device password‎‏‎‎‏‎"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‎‎SIM PIN area‎‏‎‎‏‎"</string>
@@ -69,8 +61,7 @@
<string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎SIM \"‎‏‎‎‏‏‎<xliff:g id="CARRIER">%1$s</xliff:g>‎‏‎‎‏‏‏‎\" is now disabled. Enter PUK code to continue. Contact carrier for details.‎‏‎‎‏‎"</string>
<string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎Enter desired PIN code‎‏‎‎‏‎"</string>
<string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‏‏‎‎‎Confirm desired PIN code‎‏‎‎‏‎"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (1123048780346295748) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‎Unlocking SIM…‎‏‎‎‏‎"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‎‎‏‎‎Type a PIN that is 4 to 8 numbers.‎‏‎‎‏‎"</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎PUK code should be 8 numbers or more.‎‏‎‎‏‎"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‏‏‎‏‏‎You have incorrectly typed your PIN ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ times. ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try again in ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ seconds.‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index d549ad8df9bd..03216ae4bb42 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -33,22 +33,14 @@
<string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"<xliff:g id="PERCENTAGE">%s</xliff:g> • หยุดชาร์จชั่วคราวเพื่อยืดอายุแบตเตอรี่"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"กด \"เมนู\" เพื่อปลดล็อก"</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"เครือข่ายถูกล็อก"</string>
- <!-- no translation found for keyguard_missing_sim_message_short (685029586173458728) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions (7735360104844653246) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions_long (3451467338947610268) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_message_short (3955052454216046100) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_instructions (5034635040020685428) -->
- <skip />
- <!-- no translation found for keyguard_sim_locked_message (7095293254587575270) -->
- <skip />
- <!-- no translation found for keyguard_sim_puk_locked_message (2503428315518592542) -->
- <skip />
- <!-- no translation found for keyguard_sim_unlock_progress_dialog_message (8489092646014631659) -->
- <skip />
+ <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ไม่มี SIM"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"โปรดใส่ SIM"</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ไม่มี SIM หรืออ่านไม่ได้ โปรดใส่ SIM"</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM ใช้งานไม่ได้"</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ปิดใช้งาน SIM อย่างถาวรแล้ว\n ติดต่อผู้ให้บริการไร้สายของคุณเพื่อรับ SIM ใหม่"</string>
+ <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM ถูกล็อก"</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM ถูกล็อกด้วย PUK"</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"กำลังปลดล็อก SIM…"</string>
<string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"พื้นที่ PIN"</string>
<string name="keyguard_accessibility_password" msgid="3524161948484801450">"รหัสผ่านของอุปกรณ์"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"พื้นที่ PIN ของซิม"</string>
@@ -69,8 +61,7 @@
<string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"ปิดใช้ซิม \"<xliff:g id="CARRIER">%1$s</xliff:g>\" แล้ว ป้อนรหัส PUK เพื่อดำเนินการต่อ โปรดสอบถามรายละเอียดจากผู้ให้บริการ"</string>
<string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"ป้อนรหัส PIN ที่ต้องการ"</string>
<string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"ยืนยันรหัส PIN ที่ต้องการ"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (1123048780346295748) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"กำลังปลดล็อก SIM…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"พิมพ์ PIN ซึ่งเป็นเลข 4-8 หลัก"</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"รหัส PUK ต้องเป็นตัวเลขอย่างน้อย 8 หลัก"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"คุณพิมพ์ PIN ไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว \n\nโปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%2$d</xliff:g> วินาที"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index cdc6ab0f0e66..45eaa647dfd4 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -33,22 +33,14 @@
<string name="keyguard_plugged_in_charging_limited" msgid="1657547879230699837">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Na-pause ang pag-charge para protektahan ang baterya"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pindutin ang Menu upang i-unlock."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Naka-lock ang network"</string>
- <!-- no translation found for keyguard_missing_sim_message_short (685029586173458728) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions (7735360104844653246) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_instructions_long (3451467338947610268) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_message_short (3955052454216046100) -->
- <skip />
- <!-- no translation found for keyguard_permanent_disabled_sim_instructions (5034635040020685428) -->
- <skip />
- <!-- no translation found for keyguard_sim_locked_message (7095293254587575270) -->
- <skip />
- <!-- no translation found for keyguard_sim_puk_locked_message (2503428315518592542) -->
- <skip />
- <!-- no translation found for keyguard_sim_unlock_progress_dialog_message (8489092646014631659) -->
- <skip />
+ <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Walang SIM"</string>
+ <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Magdagdag ng SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Wala o hindi nababasa ang SIM. Magdagdag ng SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Hindi magagamit na SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Permanenteng na-deactivate ang iyong SIM.\n Makipag-ugnayan sa iyong service provider ng wireless para sa isa pang SIM."</string>
+ <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Naka-lock ang SIM."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Naka-PUK lock ang SIM."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ina-unlock ang SIM…"</string>
<string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"Lugar ng PIN"</string>
<string name="keyguard_accessibility_password" msgid="3524161948484801450">"Password ng device"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"Lugar ng PIN ng SIM"</string>
@@ -69,8 +61,7 @@
<string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Naka-disable na ngayon ang SIM na \"<xliff:g id="CARRIER">%1$s</xliff:g>.\" Ilagay ang PUK code upang magpatuloy. Makipag-ugnayan sa carrier para sa mga detalye."</string>
<string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Ilagay ang gustong PIN code"</string>
<string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Kumpirmahin ang gustong PIN code"</string>
- <!-- no translation found for kg_sim_unlock_progress_dialog_message (1123048780346295748) -->
- <skip />
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Ina-unlock ang SIM…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Mag-type ng PIN na 4 hanggang 8 numero."</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"Dapat ay 8 numero o higit pa ang PUK code."</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Na-type mo nang mali ang iyong PIN nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index cb704fdefb84..f4cef84724e4 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -53,7 +53,7 @@
<string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
<!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that charging is temporarily limited. -->
- <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging paused to protect battery</string>
+ <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging optimized to protect battery</string>
<!-- On the keyguard screen, when pattern lock is disabled, only tell them to press menu to unlock. This is shown in small font at the bottom. -->
<string name="keyguard_instructions_when_pattern_disabled">Press Menu to unlock.</string>
diff --git a/packages/SystemUI/res-product/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-product/values-b+sr+Latn/strings.xml
index d85c62c13bbd..64b3750d0411 100644
--- a/packages/SystemUI/res-product/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-product/values-b+sr+Latn/strings.xml
@@ -19,32 +19,32 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dock_alignment_slow_charging" product="default" msgid="6997633396534416792">"Ponovo postavite telefon radi bržeg punjenja"</string>
- <string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Ponovo postavite telefon radi bežičnog punjenja"</string>
- <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Android TV će se uskoro isključiti. Pritisnite dugme da bi ostao uključen."</string>
- <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"Uređaj će se uskoro isključiti. Pritisnite da bi ostao uključen."</string>
+ <string name="dock_alignment_slow_charging" product="default" msgid="6997633396534416792">"Поново поставите телефон ради бржег пуњења"</string>
+ <string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Поново поставите телефон ради бежичног пуњења"</string>
+ <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Android TV ће се ускоро искључити. Притисните дугме да би остао укључен."</string>
+ <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"Уређај ће се ускоро искључити. Притисните да би остао укључен."</string>
<!-- no translation found for keyguard_missing_sim_message (408124574073032188) -->
<skip />
<!-- no translation found for keyguard_missing_sim_message (2605468359948247208) -->
<skip />
- <string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"PIN kodovi se ne podudaraju"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta, ovaj tablet će se resetovati, čime se brišu svi podaci korisnika."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta, ovaj telefon će se resetovati, čime se brišu svi podaci korisnika."</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER">%d</xliff:g> puta. Ovaj tablet će se resetovati, čime se brišu svi podaci."</string>
- <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER">%d</xliff:g> puta. Ovaj telefon će se resetovati, čime se brišu svi podaci."</string>
- <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta, uklonićemo ovog korisnika, čime se brišu svi podaci korisnika."</string>
- <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta, uklonićemo ovog korisnika, čime se brišu svi podaci korisnika."</string>
- <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER">%d</xliff:g> puta. Uklonićemo ovog korisnika, čime se brišu svi podaci korisnika."</string>
- <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER">%d</xliff:g> puta. Uklonićemo ovog korisnika, čime se brišu svi podaci korisnika."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta, uklonićemo poslovni profil, čime se brišu svi podaci sa profila."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta, uklonićemo poslovni profil, čime se brišu svi podaci sa profila."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER">%d</xliff:g> puta. Uklonićemo poslovni profil, čime se brišu svi podaci sa profila."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER">%d</xliff:g> puta. Uklonićemo poslovni profil, čime se brišu svi podaci sa profila."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Netačno ste nacrtali šablon za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta, zatražićemo da otključate tablet pomoću imejl naloga.\n\n Probajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Netačno ste nacrtali šablon za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako pogrešno pokušate još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta, zatražićemo da otključate telefon pomoću imejl naloga.\n\n Probajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
- <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Otključajte telefon za još opcija"</string>
- <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Otključajte tablet za još opcija"</string>
- <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Otključajte uređaj za još opcija"</string>
- <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Pušta se na ovom telefonu"</string>
- <string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Pušta se na ovom tabletu"</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"PIN кодови се не подударају"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Погрешно сте покушали да откључате таблет <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још <xliff:g id="NUMBER_1">%2$d</xliff:g> пута, овај таблет ће се ресетовати, чиме се бришу сви подаци корисника."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Погрешно сте покушали да откључате телефон <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још <xliff:g id="NUMBER_1">%2$d</xliff:g> пута, овај телефон ће се ресетовати, чиме се бришу сви подаци корисника."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Погрешно сте покушали да откључате таблет <xliff:g id="NUMBER">%d</xliff:g> пута. Овај таблет ће се ресетовати, чиме се бришу сви подаци."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Погрешно сте покушали да откључате телефон <xliff:g id="NUMBER">%d</xliff:g> пута. Овај телефон ће се ресетовати, чиме се бришу сви подаци."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Погрешно сте покушали да откључате таблет <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још <xliff:g id="NUMBER_1">%2$d</xliff:g> пута, уклонићемо овог корисника, чиме се бришу сви подаци корисника."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Погрешно сте покушали да откључате телефон <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још <xliff:g id="NUMBER_1">%2$d</xliff:g> пута, уклонићемо овог корисника, чиме се бришу сви подаци корисника."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Погрешно сте покушали да откључате таблет <xliff:g id="NUMBER">%d</xliff:g> пута. Уклонићемо овог корисника, чиме се бришу сви подаци корисника."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Погрешно сте покушали да откључате телефон <xliff:g id="NUMBER">%d</xliff:g> пута. Уклонићемо овог корисника, чиме се бришу сви подаци корисника."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Погрешно сте покушали да откључате таблет <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још <xliff:g id="NUMBER_1">%2$d</xliff:g> пута, уклонићемо пословни профил, чиме се бришу сви подаци са профила."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Погрешно сте покушали да откључате телефон <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још <xliff:g id="NUMBER_1">%2$d</xliff:g> пута, уклонићемо пословни профил, чиме се бришу сви подаци са профила."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Погрешно сте покушали да откључате таблет <xliff:g id="NUMBER">%d</xliff:g> пута. Уклонићемо пословни профил, чиме се бришу сви подаци са профила."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Погрешно сте покушали да откључате телефон <xliff:g id="NUMBER">%d</xliff:g> пута. Уклонићемо пословни профил, чиме се бришу сви подаци са профила."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Нетачно сте нацртали шаблон за откључавање <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још <xliff:g id="NUMBER_1">%2$d</xliff:g> пута, затражићемо да откључате таблет помоћу имејл налога.\n\n Пробајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Нетачно сте нацртали шаблон за откључавање <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. Ако погрешно покушате још <xliff:g id="NUMBER_1">%2$d</xliff:g> пута, затражићемо да откључате телефон помоћу имејл налога.\n\n Пробајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
+ <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Откључајте телефон за још опција"</string>
+ <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Откључајте таблет за још опција"</string>
+ <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Откључајте уређај за још опција"</string>
+ <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Пушта се на овом телефону"</string>
+ <string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Пушта се на овом таблету"</string>
</resources>
diff --git a/packages/SystemUI/res-product/values-en-rAU/strings.xml b/packages/SystemUI/res-product/values-en-rAU/strings.xml
index b3ddded844f5..dcc38abdb289 100644
--- a/packages/SystemUI/res-product/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-product/values-en-rAU/strings.xml
@@ -23,10 +23,8 @@
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Realign phone to charge wirelessly"</string>
<string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"The Android TV device will soon turn off; press a button to keep it on."</string>
<string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"The device will soon turn off; press to keep it on."</string>
- <!-- no translation found for keyguard_missing_sim_message (408124574073032188) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_message (2605468359948247208) -->
- <skip />
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="408124574073032188">"No SIM in tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="2605468359948247208">"No SIM in phone."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"PIN codes do not match"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, this tablet will be reset, which will delete all its data."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, this phone will be reset, which will delete all its data."</string>
diff --git a/packages/SystemUI/res-product/values-en-rCA/strings.xml b/packages/SystemUI/res-product/values-en-rCA/strings.xml
index b29aaec12c61..422a2e8208cb 100644
--- a/packages/SystemUI/res-product/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-product/values-en-rCA/strings.xml
@@ -23,10 +23,8 @@
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Realign phone to charge wirelessly"</string>
<string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"The Android TV device will soon turn off; press a button to keep it on."</string>
<string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"The device will soon turn off; press to keep it on."</string>
- <!-- no translation found for keyguard_missing_sim_message (408124574073032188) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_message (2605468359948247208) -->
- <skip />
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="408124574073032188">"No SIM in tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="2605468359948247208">"No SIM in phone."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"PIN codes does not match"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, this tablet will be reset, which will delete all its data."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, this phone will be reset, which will delete all its data."</string>
diff --git a/packages/SystemUI/res-product/values-en-rGB/strings.xml b/packages/SystemUI/res-product/values-en-rGB/strings.xml
index b3ddded844f5..dcc38abdb289 100644
--- a/packages/SystemUI/res-product/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-product/values-en-rGB/strings.xml
@@ -23,10 +23,8 @@
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Realign phone to charge wirelessly"</string>
<string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"The Android TV device will soon turn off; press a button to keep it on."</string>
<string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"The device will soon turn off; press to keep it on."</string>
- <!-- no translation found for keyguard_missing_sim_message (408124574073032188) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_message (2605468359948247208) -->
- <skip />
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="408124574073032188">"No SIM in tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="2605468359948247208">"No SIM in phone."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"PIN codes do not match"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, this tablet will be reset, which will delete all its data."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, this phone will be reset, which will delete all its data."</string>
diff --git a/packages/SystemUI/res-product/values-en-rIN/strings.xml b/packages/SystemUI/res-product/values-en-rIN/strings.xml
index b3ddded844f5..dcc38abdb289 100644
--- a/packages/SystemUI/res-product/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-product/values-en-rIN/strings.xml
@@ -23,10 +23,8 @@
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Realign phone to charge wirelessly"</string>
<string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"The Android TV device will soon turn off; press a button to keep it on."</string>
<string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"The device will soon turn off; press to keep it on."</string>
- <!-- no translation found for keyguard_missing_sim_message (408124574073032188) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_message (2605468359948247208) -->
- <skip />
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="408124574073032188">"No SIM in tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="2605468359948247208">"No SIM in phone."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"PIN codes do not match"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, this tablet will be reset, which will delete all its data."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, this phone will be reset, which will delete all its data."</string>
diff --git a/packages/SystemUI/res-product/values-en-rXC/strings.xml b/packages/SystemUI/res-product/values-en-rXC/strings.xml
index 1ab731aa95fe..98396f4b60b1 100644
--- a/packages/SystemUI/res-product/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-product/values-en-rXC/strings.xml
@@ -23,10 +23,8 @@
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‎Realign phone to charge wirelessly‎‏‎‎‏‎"</string>
<string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎The Android TV device will soon turn off; press a button to keep it on.‎‏‎‎‏‎"</string>
<string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‎The device will soon turn off; press to keep it on.‎‏‎‎‏‎"</string>
- <!-- no translation found for keyguard_missing_sim_message (408124574073032188) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_message (2605468359948247208) -->
- <skip />
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="408124574073032188">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‏‏‏‏‎‎‎No SIM in tablet.‎‏‎‎‏‎"</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="2605468359948247208">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‎‎‎No SIM in phone.‎‏‎‎‏‎"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‎‏‏‎PIN codes does not match‎‏‎‎‏‎"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎You have incorrectly attempted to unlock the tablet ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ times. After ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ more unsuccessful attempts, this tablet will be reset, which will delete all its data.‎‏‎‎‏‎"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‏‎You have incorrectly attempted to unlock the phone ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ times. After ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ more unsuccessful attempts, this phone will be reset, which will delete all its data.‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res-product/values-th/strings.xml b/packages/SystemUI/res-product/values-th/strings.xml
index ebc29df4e5b4..765ef7dd50a3 100644
--- a/packages/SystemUI/res-product/values-th/strings.xml
+++ b/packages/SystemUI/res-product/values-th/strings.xml
@@ -23,10 +23,8 @@
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"จัดวางโทรศัพท์ใหม่เพื่อชาร์จแบบไร้สาย"</string>
<string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"อุปกรณ์ Android TV จะปิดเครื่องในอีกไม่ช้า กดปุ่มเพื่อเปิดอุปกรณ์ต่อไป"</string>
<string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"อุปกรณ์จะปิดเครื่องในอีกไม่ช้า กดเพื่อเปิดอุปกรณ์ต่อไป"</string>
- <!-- no translation found for keyguard_missing_sim_message (408124574073032188) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_message (2605468359948247208) -->
- <skip />
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="408124574073032188">"ไม่มี SIM ในแท็บเล็ต"</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="2605468359948247208">"ไม่มี SIM ในโทรศัพท์"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"รหัส PIN ไม่ตรง"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"คุณปลดล็อกแท็บเล็ตไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ระบบจะรีเซ็ตแท็บเล็ตเครื่องนี้ ซึ่งจะเป็นการลบข้อมูลทั้งหมดในเครื่อง"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"คุณปลดล็อกโทรศัพท์ไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ระบบจะรีเซ็ตโทรศัพท์เครื่องนี้ ซึ่งจะเป็นการลบข้อมูลทั้งหมดในเครื่อง"</string>
diff --git a/packages/SystemUI/res-product/values-tl/strings.xml b/packages/SystemUI/res-product/values-tl/strings.xml
index f8b4fef5cdaf..df8713668153 100644
--- a/packages/SystemUI/res-product/values-tl/strings.xml
+++ b/packages/SystemUI/res-product/values-tl/strings.xml
@@ -23,10 +23,8 @@
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"I-align ulit ang telepono para i-charge nang wireless"</string>
<string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Mag-o-off na ang Android TV device; pumindot ng button para panatilihin itong naka-on."</string>
<string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"Mag-o-off na ang device; pumindot para panatilihin itong naka-on."</string>
- <!-- no translation found for keyguard_missing_sim_message (408124574073032188) -->
- <skip />
- <!-- no translation found for keyguard_missing_sim_message (2605468359948247208) -->
- <skip />
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="408124574073032188">"Walang SIM sa tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="2605468359948247208">"Walang SIM sa telepono."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"Hindi nagtutugma ang mga PIN code"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"<xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses mo nang sinubukang i-unlock ang tablet gamit ang maling password. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagsubok, ire-reset ang tablet na ito, na magiging dahilan para ma-delete ang lahat ng data nito."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"<xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses mo nang sinubukang i-unlock ang telepono gamit ang maling password. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagsubok, ire-reset ang teleponong ito, na magiging dahilan para ma-delete ang lahat ng data nito."</string>
diff --git a/packages/SystemUI/res/drawable/ic_do_not_disturb.xml b/packages/SystemUI/res/drawable/ic_do_not_disturb.xml
new file mode 100644
index 000000000000..95663c095c4b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_do_not_disturb.xml
@@ -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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM7,11h10v2L7,13z"/>
+</vector>
diff --git a/packages/SystemUI/res/values-b+sr+Latn-land/strings.xml b/packages/SystemUI/res/values-b+sr+Latn-land/strings.xml
index b956edb17c8d..992fe1cbb1e3 100644
--- a/packages/SystemUI/res/values-b+sr+Latn-land/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn-land/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="toast_rotation_locked" msgid="4914046305911646988">"Ekran je sada zaključan u vertikalnom položaju."</string>
+ <string name="toast_rotation_locked" msgid="4914046305911646988">"Екран је сада закључан у вертикалном положају."</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn-ldrtl/strings.xml b/packages/SystemUI/res/values-b+sr+Latn-ldrtl/strings.xml
index 8e5ecf906102..f4106f19face 100644
--- a/packages/SystemUI/res/values-b+sr+Latn-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn-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">"Prevucite ulevo da biste brzo promenili aplikacije"</string>
+ <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Превуците улево да бисте брзо променили апликације"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
index f72890bd5d2c..85c38ca56d91 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
@@ -19,17 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN je povezan"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Veza sa VPN-om je prekinuta"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Preko: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obaveštenja"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nema obaveštenja"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon snima"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera snima"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera i mikrofon snimaju"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Snimanje mikrofonom je zaustavljeno"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Snimanje kamerom je zaustavljeno"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Snimanje kamerom i mikrofonom je zaustavljeno"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Snimanje ekrana je započeto"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Snimanje ekrana je zaustavljeno"</string>
+ <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN је повезан"</string>
+ <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Веза са VPN-ом је прекинута"</string>
+ <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Преко: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="tv_notification_panel_title" msgid="5311050946506276154">"Обавештења"</string>
+ <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Нема обавештења"</string>
+ <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофон снима"</string>
+ <string name="camera_recording_announcement" msgid="7240177719403759112">"Камера снима"</string>
+ <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камера и микрофон снимају"</string>
+ <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Снимање микрофоном је заустављено"</string>
+ <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Снимање камером је заустављено"</string>
+ <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Снимање камером и микрофоном је заустављено"</string>
+ <string name="screen_recording_announcement" msgid="2996750593472241520">"Снимање екрана је започето"</string>
+ <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Снимање екрана је заустављено"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index b69b06419281..dace491993ba 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -32,148 +32,148 @@
<!-- no translation found for tile_states_default:1 (7086813178962737808) -->
<!-- no translation found for tile_states_default:2 (9192445505551219506) -->
<string-array name="tile_states_internet">
- <item msgid="5499482407653291407">"Nedostupno"</item>
- <item msgid="3048856902433862868">"Isključeno"</item>
- <item msgid="6877982264300789870">"Uključeno"</item>
+ <item msgid="5499482407653291407">"Недоступно"</item>
+ <item msgid="3048856902433862868">"Искључено"</item>
+ <item msgid="6877982264300789870">"Укључено"</item>
</string-array>
<string-array name="tile_states_wifi">
- <item msgid="8054147400538405410">"Nedostupno"</item>
- <item msgid="4293012229142257455">"Isključeno"</item>
- <item msgid="6221288736127914861">"Uključeno"</item>
+ <item msgid="8054147400538405410">"Недоступно"</item>
+ <item msgid="4293012229142257455">"Искључено"</item>
+ <item msgid="6221288736127914861">"Укључено"</item>
</string-array>
<string-array name="tile_states_cell">
- <item msgid="1235899788959500719">"Nedostupno"</item>
- <item msgid="2074416252859094119">"Isključeno"</item>
- <item msgid="287997784730044767">"Uključeno"</item>
+ <item msgid="1235899788959500719">"Недоступно"</item>
+ <item msgid="2074416252859094119">"Искључено"</item>
+ <item msgid="287997784730044767">"Укључено"</item>
</string-array>
<string-array name="tile_states_battery">
- <item msgid="6311253873330062961">"Nedostupno"</item>
- <item msgid="7838121007534579872">"Isključeno"</item>
- <item msgid="1578872232501319194">"Uključeno"</item>
+ <item msgid="6311253873330062961">"Недоступно"</item>
+ <item msgid="7838121007534579872">"Искључено"</item>
+ <item msgid="1578872232501319194">"Укључено"</item>
</string-array>
<string-array name="tile_states_dnd">
- <item msgid="467587075903158357">"Nedostupno"</item>
- <item msgid="5376619709702103243">"Isključeno"</item>
- <item msgid="4875147066469902392">"Uključeno"</item>
+ <item msgid="467587075903158357">"Недоступно"</item>
+ <item msgid="5376619709702103243">"Искључено"</item>
+ <item msgid="4875147066469902392">"Укључено"</item>
</string-array>
<string-array name="tile_states_flashlight">
- <item msgid="3465257127433353857">"Nedostupno"</item>
- <item msgid="5044688398303285224">"Isključeno"</item>
- <item msgid="8527389108867454098">"Uključeno"</item>
+ <item msgid="3465257127433353857">"Недоступно"</item>
+ <item msgid="5044688398303285224">"Искључено"</item>
+ <item msgid="8527389108867454098">"Укључено"</item>
</string-array>
<string-array name="tile_states_rotation">
- <item msgid="4578491772376121579">"Nedostupno"</item>
- <item msgid="5776427577477729185">"Isključeno"</item>
- <item msgid="7105052717007227415">"Uključeno"</item>
+ <item msgid="4578491772376121579">"Недоступно"</item>
+ <item msgid="5776427577477729185">"Искључено"</item>
+ <item msgid="7105052717007227415">"Укључено"</item>
</string-array>
<string-array name="tile_states_bt">
- <item msgid="5330252067413512277">"Nedostupno"</item>
- <item msgid="5315121904534729843">"Isključeno"</item>
- <item msgid="503679232285959074">"Uključeno"</item>
+ <item msgid="5330252067413512277">"Недоступно"</item>
+ <item msgid="5315121904534729843">"Искључено"</item>
+ <item msgid="503679232285959074">"Укључено"</item>
</string-array>
<string-array name="tile_states_airplane">
- <item msgid="1985366811411407764">"Nedostupno"</item>
- <item msgid="4801037224991420996">"Isključeno"</item>
- <item msgid="1982293347302546665">"Uključeno"</item>
+ <item msgid="1985366811411407764">"Недоступно"</item>
+ <item msgid="4801037224991420996">"Искључено"</item>
+ <item msgid="1982293347302546665">"Укључено"</item>
</string-array>
<string-array name="tile_states_location">
- <item msgid="3316542218706374405">"Nedostupno"</item>
- <item msgid="4813655083852587017">"Isključeno"</item>
- <item msgid="6744077414775180687">"Uključeno"</item>
+ <item msgid="3316542218706374405">"Недоступно"</item>
+ <item msgid="4813655083852587017">"Искључено"</item>
+ <item msgid="6744077414775180687">"Укључено"</item>
</string-array>
<string-array name="tile_states_hotspot">
- <item msgid="3145597331197351214">"Nedostupno"</item>
- <item msgid="5715725170633593906">"Isključeno"</item>
- <item msgid="2075645297847971154">"Uključeno"</item>
+ <item msgid="3145597331197351214">"Недоступно"</item>
+ <item msgid="5715725170633593906">"Искључено"</item>
+ <item msgid="2075645297847971154">"Укључено"</item>
</string-array>
<string-array name="tile_states_color_correction">
- <item msgid="2840507878437297682">"Nedostupno"</item>
- <item msgid="1909756493418256167">"Isključeno"</item>
- <item msgid="4531508423703413340">"Uključeno"</item>
+ <item msgid="2840507878437297682">"Недоступно"</item>
+ <item msgid="1909756493418256167">"Искључено"</item>
+ <item msgid="4531508423703413340">"Укључено"</item>
</string-array>
<string-array name="tile_states_inversion">
- <item msgid="3638187931191394628">"Nedostupno"</item>
- <item msgid="9103697205127645916">"Isključeno"</item>
- <item msgid="8067744885820618230">"Uključeno"</item>
+ <item msgid="3638187931191394628">"Недоступно"</item>
+ <item msgid="9103697205127645916">"Искључено"</item>
+ <item msgid="8067744885820618230">"Укључено"</item>
</string-array>
<string-array name="tile_states_saver">
- <item msgid="39714521631367660">"Nedostupno"</item>
- <item msgid="6983679487661600728">"Isključeno"</item>
- <item msgid="7520663805910678476">"Uključeno"</item>
+ <item msgid="39714521631367660">"Недоступно"</item>
+ <item msgid="6983679487661600728">"Искључено"</item>
+ <item msgid="7520663805910678476">"Укључено"</item>
</string-array>
<string-array name="tile_states_dark">
- <item msgid="2762596907080603047">"Nedostupno"</item>
- <item msgid="400477985171353">"Isključeno"</item>
- <item msgid="630890598801118771">"Uključeno"</item>
+ <item msgid="2762596907080603047">"Недоступно"</item>
+ <item msgid="400477985171353">"Искључено"</item>
+ <item msgid="630890598801118771">"Укључено"</item>
</string-array>
<string-array name="tile_states_work">
- <item msgid="389523503690414094">"Nedostupno"</item>
- <item msgid="8045580926543311193">"Isključeno"</item>
- <item msgid="4913460972266982499">"Uključeno"</item>
+ <item msgid="389523503690414094">"Недоступно"</item>
+ <item msgid="8045580926543311193">"Искључено"</item>
+ <item msgid="4913460972266982499">"Укључено"</item>
</string-array>
<string-array name="tile_states_cast">
- <item msgid="6032026038702435350">"Nedostupno"</item>
- <item msgid="1488620600954313499">"Isključeno"</item>
- <item msgid="588467578853244035">"Uključeno"</item>
+ <item msgid="6032026038702435350">"Недоступно"</item>
+ <item msgid="1488620600954313499">"Искључено"</item>
+ <item msgid="588467578853244035">"Укључено"</item>
</string-array>
<string-array name="tile_states_night">
- <item msgid="7857498964264855466">"Nedostupno"</item>
- <item msgid="2744885441164350155">"Isključeno"</item>
- <item msgid="151121227514952197">"Uključeno"</item>
+ <item msgid="7857498964264855466">"Недоступно"</item>
+ <item msgid="2744885441164350155">"Искључено"</item>
+ <item msgid="151121227514952197">"Укључено"</item>
</string-array>
<string-array name="tile_states_screenrecord">
- <item msgid="1085836626613341403">"Nedostupno"</item>
- <item msgid="8259411607272330225">"Isključeno"</item>
- <item msgid="578444932039713369">"Uključeno"</item>
+ <item msgid="1085836626613341403">"Недоступно"</item>
+ <item msgid="8259411607272330225">"Искључено"</item>
+ <item msgid="578444932039713369">"Укључено"</item>
</string-array>
<string-array name="tile_states_reverse">
- <item msgid="3574611556622963971">"Nedostupno"</item>
- <item msgid="8707481475312432575">"Isključeno"</item>
- <item msgid="8031106212477483874">"Uključeno"</item>
+ <item msgid="3574611556622963971">"Недоступно"</item>
+ <item msgid="8707481475312432575">"Искључено"</item>
+ <item msgid="8031106212477483874">"Укључено"</item>
</string-array>
<string-array name="tile_states_reduce_brightness">
- <item msgid="1839836132729571766">"Nedostupno"</item>
- <item msgid="4572245614982283078">"Isključeno"</item>
- <item msgid="6536448410252185664">"Uključeno"</item>
+ <item msgid="1839836132729571766">"Недоступно"</item>
+ <item msgid="4572245614982283078">"Искључено"</item>
+ <item msgid="6536448410252185664">"Укључено"</item>
</string-array>
<string-array name="tile_states_cameratoggle">
- <item msgid="6680671247180519913">"Nedostupno"</item>
- <item msgid="4765607635752003190">"Isključeno"</item>
- <item msgid="1697460731949649844">"Uključeno"</item>
+ <item msgid="6680671247180519913">"Недоступно"</item>
+ <item msgid="4765607635752003190">"Искључено"</item>
+ <item msgid="1697460731949649844">"Укључено"</item>
</string-array>
<string-array name="tile_states_mictoggle">
- <item msgid="6895831614067195493">"Nedostupno"</item>
- <item msgid="3296179158646568218">"Isključeno"</item>
- <item msgid="8998632451221157987">"Uključeno"</item>
+ <item msgid="6895831614067195493">"Недоступно"</item>
+ <item msgid="3296179158646568218">"Искључено"</item>
+ <item msgid="8998632451221157987">"Укључено"</item>
</string-array>
<string-array name="tile_states_controls">
- <item msgid="8199009425335668294">"Nedostupno"</item>
- <item msgid="4544919905196727508">"Isključeno"</item>
- <item msgid="3422023746567004609">"Uključeno"</item>
+ <item msgid="8199009425335668294">"Недоступно"</item>
+ <item msgid="4544919905196727508">"Искључено"</item>
+ <item msgid="3422023746567004609">"Укључено"</item>
</string-array>
<string-array name="tile_states_wallet">
- <item msgid="4177615438710836341">"Nedostupno"</item>
- <item msgid="7571394439974244289">"Isključeno"</item>
- <item msgid="6866424167599381915">"Uključeno"</item>
+ <item msgid="4177615438710836341">"Недоступно"</item>
+ <item msgid="7571394439974244289">"Искључено"</item>
+ <item msgid="6866424167599381915">"Укључено"</item>
</string-array>
<string-array name="tile_states_qr_code_scanner">
- <item msgid="7435143266149257618">"Nedostupno"</item>
- <item msgid="3301403109049256043">"Isključeno"</item>
- <item msgid="8878684975184010135">"Uključeno"</item>
+ <item msgid="7435143266149257618">"Недоступно"</item>
+ <item msgid="3301403109049256043">"Искључено"</item>
+ <item msgid="8878684975184010135">"Укључено"</item>
</string-array>
<string-array name="tile_states_alarm">
- <item msgid="4936533380177298776">"Nedostupno"</item>
- <item msgid="2710157085538036590">"Isključeno"</item>
- <item msgid="7809470840976856149">"Uključeno"</item>
+ <item msgid="4936533380177298776">"Недоступно"</item>
+ <item msgid="2710157085538036590">"Искључено"</item>
+ <item msgid="7809470840976856149">"Укључено"</item>
</string-array>
<string-array name="tile_states_onehanded">
- <item msgid="8189342855739930015">"Nedostupno"</item>
- <item msgid="146088982397753810">"Isključeno"</item>
- <item msgid="460891964396502657">"Uključeno"</item>
+ <item msgid="8189342855739930015">"Недоступно"</item>
+ <item msgid="146088982397753810">"Искључено"</item>
+ <item msgid="460891964396502657">"Укључено"</item>
</string-array>
<string-array name="tile_states_dream">
- <item msgid="6184819793571079513">"Nedostupno"</item>
- <item msgid="8014986104355098744">"Isključeno"</item>
- <item msgid="5966994759929723339">"Uključeno"</item>
+ <item msgid="6184819793571079513">"Недоступно"</item>
+ <item msgid="8014986104355098744">"Искључено"</item>
+ <item msgid="5966994759929723339">"Укључено"</item>
</string-array>
</resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index fd2e3249d59a..c5ffc94d01af 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -30,6 +30,9 @@
<bool name="flag_charging_ripple">false</bool>
+ <!-- Whether to show chipbar UI whenever the device is unlocked by ActiveUnlock. -->
+ <bool name="flag_active_unlock_chipbar">true</bool>
+
<bool name="flag_smartspace">false</bool>
<!-- Whether the user switcher chip shows in the status bar. When true, the multi user
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 61f82dfd7eac..4f64a4bc2b16 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2091,6 +2091,9 @@
<!-- Label for when Do not disturb is off in QS detail panel [CHAR LIMIT=NONE] -->
<string name="dnd_is_off">Do Not Disturb is off</string>
+ <!-- Label for when Do not disturb is on in lockscreen quick affordance [CHAR LIMIT=NONE] -->
+ <string name="dnd_is_on">Do Not Disturb is on</string>
+
<!-- Prompt for when Do not disturb is on from automatic rule in QS [CHAR LIMIT=NONE] -->
<string name="qs_dnd_prompt_auto_rule">Do Not Disturb was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>).</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 7da27b1d6898..baaef1983e9c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -103,6 +103,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
@Override
public void reset() {
+ super.reset();
// start fresh
mDismissing = false;
mView.resetPasswordText(false /* animate */, false /* announce */);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardActiveUnlockModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardActiveUnlockModel.kt
new file mode 100644
index 000000000000..3a89c13ddd64
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardActiveUnlockModel.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.annotation.CurrentTimeMillisLong
+import com.android.systemui.dump.DumpsysTableLogger
+import com.android.systemui.dump.Row
+import com.android.systemui.plugins.util.RingBuffer
+
+/** Verbose debug information. */
+data class KeyguardActiveUnlockModel(
+ @CurrentTimeMillisLong override var timeMillis: Long = 0L,
+ override var userId: Int = 0,
+ override var listening: Boolean = false,
+ // keep sorted
+ var awakeKeyguard: Boolean = false,
+ var authInterruptActive: Boolean = false,
+ var fpLockedOut: Boolean = false,
+ var primaryAuthRequired: Boolean = false,
+ var switchingUser: Boolean = false,
+ var triggerActiveUnlockForAssistant: Boolean = false,
+ var userCanDismissLockScreen: Boolean = false,
+) : KeyguardListenModel() {
+
+ /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */
+ val asStringList: List<String> by lazy {
+ listOf(
+ DATE_FORMAT.format(timeMillis),
+ timeMillis.toString(),
+ userId.toString(),
+ listening.toString(),
+ // keep sorted
+ awakeKeyguard.toString(),
+ authInterruptActive.toString(),
+ fpLockedOut.toString(),
+ primaryAuthRequired.toString(),
+ switchingUser.toString(),
+ triggerActiveUnlockForAssistant.toString(),
+ userCanDismissLockScreen.toString(),
+ )
+ }
+
+ /**
+ * [RingBuffer] to store [KeyguardActiveUnlockModel]. After the buffer is full, it will recycle
+ * old events.
+ *
+ * Do not use [append] to add new elements. Instead use [insert], as it will recycle if
+ * necessary.
+ */
+ class Buffer {
+ private val buffer = RingBuffer(CAPACITY) { KeyguardActiveUnlockModel() }
+
+ fun insert(model: KeyguardActiveUnlockModel) {
+ buffer.advance().apply {
+ timeMillis = model.timeMillis
+ userId = model.userId
+ listening = model.listening
+ // keep sorted
+ awakeKeyguard = model.awakeKeyguard
+ authInterruptActive = model.authInterruptActive
+ fpLockedOut = model.fpLockedOut
+ primaryAuthRequired = model.primaryAuthRequired
+ switchingUser = model.switchingUser
+ triggerActiveUnlockForAssistant = model.triggerActiveUnlockForAssistant
+ userCanDismissLockScreen = model.userCanDismissLockScreen
+ }
+ }
+
+ /**
+ * Returns the content of the buffer (sorted from latest to newest).
+ *
+ * @see KeyguardFingerprintListenModel.asStringList
+ */
+ fun toList(): List<Row> {
+ return buffer.asSequence().map { it.asStringList }.toList()
+ }
+ }
+
+ companion object {
+ const val CAPACITY = 20 // number of logs to retain
+
+ /** Headers for dumping a table using [DumpsysTableLogger]. */
+ @JvmField
+ val TABLE_HEADERS =
+ listOf(
+ "timestamp",
+ "time_millis",
+ "userId",
+ "listening",
+ // keep sorted
+ "awakeKeyguard",
+ "authInterruptActive",
+ "fpLockedOut",
+ "primaryAuthRequired",
+ "switchingUser",
+ "triggerActiveUnlockForAssistant",
+ "userCanDismissLockScreen",
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
new file mode 100644
index 000000000000..deead1959b8a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -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 com.android.keyguard
+
+import android.annotation.CurrentTimeMillisLong
+import com.android.systemui.dump.DumpsysTableLogger
+import com.android.systemui.dump.Row
+import com.android.systemui.plugins.util.RingBuffer
+
+/** Verbose debug information associated. */
+data class KeyguardFaceListenModel(
+ @CurrentTimeMillisLong override var timeMillis: Long = 0L,
+ override var userId: Int = 0,
+ override var listening: Boolean = false,
+ // keep sorted
+ var authInterruptActive: Boolean = false,
+ var biometricSettingEnabledForUser: Boolean = false,
+ var bouncerFullyShown: Boolean = false,
+ var faceAndFpNotAuthenticated: Boolean = false,
+ var faceAuthAllowed: Boolean = false,
+ var faceDisabled: Boolean = false,
+ var faceLockedOut: Boolean = false,
+ var goingToSleep: Boolean = false,
+ var keyguardAwake: Boolean = false,
+ var keyguardGoingAway: Boolean = false,
+ var listeningForFaceAssistant: Boolean = false,
+ var occludingAppRequestingFaceAuth: Boolean = false,
+ var primaryUser: Boolean = false,
+ var secureCameraLaunched: Boolean = false,
+ var supportsDetect: Boolean = false,
+ var switchingUser: Boolean = false,
+ var udfpsBouncerShowing: Boolean = false,
+ var udfpsFingerDown: Boolean = false,
+ var userNotTrustedOrDetectionIsNeeded: Boolean = false,
+) : KeyguardListenModel() {
+
+ /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */
+ val asStringList: List<String> by lazy {
+ listOf(
+ DATE_FORMAT.format(timeMillis),
+ timeMillis.toString(),
+ userId.toString(),
+ listening.toString(),
+ // keep sorted
+ authInterruptActive.toString(),
+ biometricSettingEnabledForUser.toString(),
+ bouncerFullyShown.toString(),
+ faceAndFpNotAuthenticated.toString(),
+ faceAuthAllowed.toString(),
+ faceDisabled.toString(),
+ faceLockedOut.toString(),
+ goingToSleep.toString(),
+ keyguardAwake.toString(),
+ keyguardGoingAway.toString(),
+ listeningForFaceAssistant.toString(),
+ occludingAppRequestingFaceAuth.toString(),
+ primaryUser.toString(),
+ secureCameraLaunched.toString(),
+ supportsDetect.toString(),
+ switchingUser.toString(),
+ udfpsBouncerShowing.toString(),
+ udfpsFingerDown.toString(),
+ userNotTrustedOrDetectionIsNeeded.toString(),
+ )
+ }
+
+ /**
+ * [RingBuffer] to store [KeyguardFaceListenModel]. After the buffer is full, it will recycle
+ * old events.
+ *
+ * Do not use [append] to add new elements. Instead use [insert], as it will recycle if
+ * necessary.
+ */
+ class Buffer {
+ private val buffer = RingBuffer(CAPACITY) { KeyguardFaceListenModel() }
+
+ fun insert(model: KeyguardFaceListenModel) {
+ buffer.advance().apply {
+ timeMillis = model.timeMillis
+ userId = model.userId
+ listening = model.listening
+ // keep sorted
+ biometricSettingEnabledForUser = model.biometricSettingEnabledForUser
+ bouncerFullyShown = model.bouncerFullyShown
+ faceAndFpNotAuthenticated = model.faceAndFpNotAuthenticated
+ faceAuthAllowed = model.faceAuthAllowed
+ faceDisabled = model.faceDisabled
+ faceLockedOut = model.faceLockedOut
+ goingToSleep = model.goingToSleep
+ keyguardAwake = model.keyguardAwake
+ goingToSleep = model.goingToSleep
+ keyguardGoingAway = model.keyguardGoingAway
+ listeningForFaceAssistant = model.listeningForFaceAssistant
+ occludingAppRequestingFaceAuth = model.occludingAppRequestingFaceAuth
+ primaryUser = model.primaryUser
+ secureCameraLaunched = model.secureCameraLaunched
+ supportsDetect = model.supportsDetect
+ switchingUser = model.switchingUser
+ udfpsBouncerShowing = model.udfpsBouncerShowing
+ switchingUser = model.switchingUser
+ udfpsFingerDown = model.udfpsFingerDown
+ userNotTrustedOrDetectionIsNeeded = model.userNotTrustedOrDetectionIsNeeded
+ }
+ }
+ /**
+ * Returns the content of the buffer (sorted from latest to newest).
+ *
+ * @see KeyguardFingerprintListenModel.asStringList
+ */
+ fun toList(): List<Row> {
+ return buffer.asSequence().map { it.asStringList }.toList()
+ }
+ }
+
+ companion object {
+ const val CAPACITY = 40 // number of logs to retain
+
+ /** Headers for dumping a table using [DumpsysTableLogger]. */
+ @JvmField
+ val TABLE_HEADERS =
+ listOf(
+ "timestamp",
+ "time_millis",
+ "userId",
+ "listening",
+ // keep sorted
+ "authInterruptActive",
+ "biometricSettingEnabledForUser",
+ "bouncerFullyShown",
+ "faceAndFpNotAuthenticated",
+ "faceAuthAllowed",
+ "faceDisabled",
+ "faceLockedOut",
+ "goingToSleep",
+ "keyguardAwake",
+ "keyguardGoingAway",
+ "listeningForFaceAssistant",
+ "occludingAppRequestingFaceAuth",
+ "primaryUser",
+ "secureCameraLaunched",
+ "supportsDetect",
+ "switchingUser",
+ "udfpsBouncerShowing",
+ "udfpsFingerDown",
+ "userNotTrustedOrDetectionIsNeeded",
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
new file mode 100644
index 000000000000..998dc09aa5ab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.annotation.CurrentTimeMillisLong
+import com.android.systemui.dump.DumpsysTableLogger
+import com.android.systemui.dump.Row
+import com.android.systemui.plugins.util.RingBuffer
+
+/** Verbose debug information. */
+data class KeyguardFingerprintListenModel(
+ @CurrentTimeMillisLong override var timeMillis: Long = 0L,
+ override var userId: Int = 0,
+ override var listening: Boolean = false,
+ // keepSorted
+ var biometricEnabledForUser: Boolean = false,
+ var bouncerIsOrWillShow: Boolean = false,
+ var canSkipBouncer: Boolean = false,
+ var credentialAttempted: Boolean = false,
+ var deviceInteractive: Boolean = false,
+ var dreaming: Boolean = false,
+ var fingerprintDisabled: Boolean = false,
+ var fingerprintLockedOut: Boolean = false,
+ var goingToSleep: Boolean = false,
+ var keyguardGoingAway: Boolean = false,
+ var keyguardIsVisible: Boolean = false,
+ var keyguardOccluded: Boolean = false,
+ var occludingAppRequestingFp: Boolean = false,
+ var primaryUser: Boolean = false,
+ var shouldListenSfpsState: Boolean = false,
+ var shouldListenForFingerprintAssistant: Boolean = false,
+ var strongerAuthRequired: Boolean = false,
+ var switchingUser: Boolean = false,
+ var udfps: Boolean = false,
+ var userDoesNotHaveTrust: Boolean = false,
+) : KeyguardListenModel() {
+
+ /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */
+ val asStringList: List<String> by lazy {
+ listOf(
+ DATE_FORMAT.format(timeMillis),
+ timeMillis.toString(),
+ userId.toString(),
+ listening.toString(),
+ // keep sorted
+ biometricEnabledForUser.toString(),
+ bouncerIsOrWillShow.toString(),
+ canSkipBouncer.toString(),
+ credentialAttempted.toString(),
+ deviceInteractive.toString(),
+ dreaming.toString(),
+ fingerprintDisabled.toString(),
+ fingerprintLockedOut.toString(),
+ goingToSleep.toString(),
+ keyguardGoingAway.toString(),
+ keyguardIsVisible.toString(),
+ keyguardOccluded.toString(),
+ occludingAppRequestingFp.toString(),
+ primaryUser.toString(),
+ shouldListenSfpsState.toString(),
+ shouldListenForFingerprintAssistant.toString(),
+ strongerAuthRequired.toString(),
+ switchingUser.toString(),
+ udfps.toString(),
+ userDoesNotHaveTrust.toString(),
+ )
+ }
+
+ /**
+ * [RingBuffer] to store [KeyguardFingerprintListenModel]. After the buffer is full, it will
+ * recycle old events.
+ *
+ * Do not use [append] to add new elements. Instead use [insert], as it will recycle if
+ * necessary.
+ */
+ class Buffer {
+ private val buffer = RingBuffer(CAPACITY) { KeyguardFingerprintListenModel() }
+
+ fun insert(model: KeyguardFingerprintListenModel) {
+ buffer.advance().apply {
+ timeMillis = model.timeMillis
+ userId = model.userId
+ listening = model.listening
+ // keep sorted
+ biometricEnabledForUser = model.biometricEnabledForUser
+ bouncerIsOrWillShow = model.bouncerIsOrWillShow
+ canSkipBouncer = model.canSkipBouncer
+ credentialAttempted = model.credentialAttempted
+ deviceInteractive = model.deviceInteractive
+ dreaming = model.dreaming
+ fingerprintDisabled = model.fingerprintDisabled
+ fingerprintLockedOut = model.fingerprintLockedOut
+ goingToSleep = model.goingToSleep
+ keyguardGoingAway = model.keyguardGoingAway
+ keyguardIsVisible = model.keyguardIsVisible
+ keyguardOccluded = model.keyguardOccluded
+ occludingAppRequestingFp = model.occludingAppRequestingFp
+ primaryUser = model.primaryUser
+ shouldListenSfpsState = model.shouldListenSfpsState
+ shouldListenForFingerprintAssistant = model.shouldListenForFingerprintAssistant
+ strongerAuthRequired = model.strongerAuthRequired
+ switchingUser = model.switchingUser
+ udfps = model.udfps
+ userDoesNotHaveTrust = model.userDoesNotHaveTrust
+ }
+ }
+
+ /**
+ * Returns the content of the buffer (sorted from latest to newest).
+ *
+ * @see KeyguardFingerprintListenModel.asStringList
+ */
+ fun toList(): List<Row> {
+ return buffer.asSequence().map { it.asStringList }.toList()
+ }
+ }
+
+ companion object {
+ const val CAPACITY = 20 // number of logs to retain
+
+ /** Headers for dumping a table using [DumpsysTableLogger]. */
+ @JvmField
+ val TABLE_HEADERS =
+ listOf(
+ "timestamp",
+ "time_millis",
+ "userId",
+ "listening",
+ // keep sorted
+ "biometricAllowedForUser",
+ "bouncerIsOrWillShow",
+ "canSkipBouncer",
+ "credentialAttempted",
+ "deviceInteractive",
+ "dreaming",
+ "fingerprintDisabled",
+ "fingerprintLockedOut",
+ "goingToSleep",
+ "keyguardGoingAway",
+ "keyguardIsVisible",
+ "keyguardOccluded",
+ "occludingAppRequestingFp",
+ "primaryUser",
+ "shouldListenSidFingerprintState",
+ "shouldListenForFingerprintAssistant",
+ "strongAuthRequired",
+ "switchingUser",
+ "underDisplayFingerprint",
+ "userDoesNotHaveTrust",
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 2b660dee4f16..d4ca8e34fb32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -67,8 +67,12 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
private final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
- public void onTrustGrantedForCurrentUser(boolean dismissKeyguard,
- TrustGrantFlags flags, String message) {
+ public void onTrustGrantedForCurrentUser(
+ boolean dismissKeyguard,
+ boolean newlyUnlocked,
+ TrustGrantFlags flags,
+ String message
+ ) {
if (dismissKeyguard) {
if (!mView.isVisibleToUser()) {
// The trust agent dismissed the keyguard without the user proving
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index d1c9a3090860..b143c5b90373 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -121,6 +121,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
@Override
public void reset() {
+ mMessageAreaController.setMessage("", false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 52ca1668e4c9..1296cf3b1f33 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -1,87 +1,32 @@
+/*
+ * 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.annotation.CurrentTimeMillisLong
+import java.text.SimpleDateFormat
+import java.util.Locale
/** Verbose logging for various keyguard listening states. */
sealed class KeyguardListenModel {
/** Timestamp of the state change. */
- abstract val timeMillis: Long
+ abstract var timeMillis: Long
/** Current user. */
- abstract val userId: Int
+ abstract var userId: Int
/** If keyguard is listening for the modality represented by this model. */
- abstract val listening: Boolean
+ abstract var listening: Boolean
}
-/**
- * Verbose debug information associated with [KeyguardUpdateMonitor.shouldListenForFingerprint].
- */
-data class KeyguardFingerprintListenModel(
- @CurrentTimeMillisLong override val timeMillis: Long,
- override val userId: Int,
- override val listening: Boolean,
- // keep sorted
- val biometricEnabledForUser: Boolean,
- val bouncerIsOrWillShow: Boolean,
- val canSkipBouncer: Boolean,
- val credentialAttempted: Boolean,
- val deviceInteractive: Boolean,
- val dreaming: Boolean,
- val fingerprintDisabled: Boolean,
- val fingerprintLockedOut: Boolean,
- val goingToSleep: Boolean,
- val keyguardGoingAway: Boolean,
- val keyguardIsVisible: Boolean,
- val keyguardOccluded: Boolean,
- val occludingAppRequestingFp: Boolean,
- val primaryUser: Boolean,
- val shouldListenSfpsState: Boolean,
- val shouldListenForFingerprintAssistant: Boolean,
- val strongerAuthRequired: Boolean,
- val switchingUser: Boolean,
- val udfps: Boolean,
- val userDoesNotHaveTrust: Boolean
-) : KeyguardListenModel()
-/**
- * Verbose debug information associated with [KeyguardUpdateMonitor.shouldListenForFace].
- */
-data class KeyguardFaceListenModel(
- @CurrentTimeMillisLong override val timeMillis: Long,
- override val userId: Int,
- override val listening: Boolean,
- // keep sorted
- val authInterruptActive: Boolean,
- val biometricSettingEnabledForUser: Boolean,
- val bouncerFullyShown: Boolean,
- val faceAndFpNotAuthenticated: Boolean,
- val faceAuthAllowed: Boolean,
- val faceDisabled: Boolean,
- val faceLockedOut: Boolean,
- val goingToSleep: Boolean,
- val keyguardAwake: Boolean,
- val keyguardGoingAway: Boolean,
- val listeningForFaceAssistant: Boolean,
- val occludingAppRequestingFaceAuth: Boolean,
- val primaryUser: Boolean,
- val secureCameraLaunched: Boolean,
- val supportsDetect: Boolean,
- val switchingUser: Boolean,
- val udfpsBouncerShowing: Boolean,
- val udfpsFingerDown: Boolean,
- val userNotTrustedOrDetectionIsNeeded: Boolean,
- ) : KeyguardListenModel()
-/**
- * Verbose debug information associated with [KeyguardUpdateMonitor.shouldTriggerActiveUnlock].
- */
-data class KeyguardActiveUnlockModel(
- @CurrentTimeMillisLong override val timeMillis: Long,
- override val userId: Int,
- override val listening: Boolean,
- // keep sorted
- val awakeKeyguard: Boolean,
- val authInterruptActive: Boolean,
- val fpLockedOut: Boolean,
- val primaryAuthRequired: Boolean,
- val switchingUser: Boolean,
- val triggerActiveUnlockForAssistant: Boolean,
- val userCanDismissLockScreen: Boolean
-) : KeyguardListenModel()
+val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
deleted file mode 100644
index 210f5e763911..000000000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
+++ /dev/null
@@ -1,73 +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.keyguard
-
-import androidx.annotation.VisibleForTesting
-import java.io.PrintWriter
-import java.text.DateFormat
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-import kotlin.collections.ArrayDeque
-
-private val DEFAULT_FORMATTING = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
-
-/** Queue for verbose logging checks for the listening state. */
-class KeyguardListenQueue(
- val sizePerModality: Int = 20
-) {
- private val faceQueue = ArrayDeque<KeyguardFaceListenModel>()
- private val fingerprintQueue = ArrayDeque<KeyguardFingerprintListenModel>()
- private val activeUnlockQueue = ArrayDeque<KeyguardActiveUnlockModel>()
-
- @get:VisibleForTesting val models: List<KeyguardListenModel>
- get() = faceQueue + fingerprintQueue + activeUnlockQueue
-
- /** Push a [model] to the queue (will be logged until the queue exceeds [sizePerModality]). */
- fun add(model: KeyguardListenModel) {
- val queue = when (model) {
- is KeyguardFaceListenModel -> faceQueue.apply { add(model) }
- is KeyguardFingerprintListenModel -> fingerprintQueue.apply { add(model) }
- is KeyguardActiveUnlockModel -> activeUnlockQueue.apply { add(model) }
- }
-
- if (queue.size > sizePerModality) {
- queue.removeFirstOrNull()
- }
- }
-
- /** Print verbose logs via the [writer]. */
- @JvmOverloads
- fun print(writer: PrintWriter, dateFormat: DateFormat = DEFAULT_FORMATTING) {
- val stringify: (KeyguardListenModel) -> String = { model ->
- " ${dateFormat.format(Date(model.timeMillis))} $model"
- }
-
- writer.println(" Face listen results (last ${faceQueue.size} calls):")
- for (model in faceQueue) {
- writer.println(stringify(model))
- }
- writer.println(" Fingerprint listen results (last ${fingerprintQueue.size} calls):")
- for (model in fingerprintQueue) {
- writer.println(stringify(model))
- }
- writer.println(" Active unlock triggers (last ${activeUnlockQueue.size} calls):")
- for (model in activeUnlockQueue) {
- writer.println(stringify(model))
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 67e3400670ba..061ca4f7d850 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -177,8 +177,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
@Override
public void startAppearAnimation() {
- setAlpha(1f);
- setTranslationY(0);
if (mAppearAnimator.isRunning()) {
mAppearAnimator.cancel();
}
@@ -215,6 +213,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
/** Animate subviews according to expansion or time. */
private void animate(float progress) {
+ setAlpha(progress);
Interpolator standardDecelerate = Interpolators.STANDARD_DECELERATE;
Interpolator legacyDecelerate = Interpolators.LEGACY_DECELERATE;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 746616a77a3a..e3c58ce1e061 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -145,6 +145,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
@@ -445,14 +446,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray();
private final Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<>();
+ private final KeyguardFingerprintListenModel.Buffer mFingerprintListenBuffer =
+ new KeyguardFingerprintListenModel.Buffer();
+ private final KeyguardFaceListenModel.Buffer mFaceListenBuffer =
+ new KeyguardFaceListenModel.Buffer();
+ private final KeyguardActiveUnlockModel.Buffer mActiveUnlockTriggerBuffer =
+ new KeyguardActiveUnlockModel.Buffer();
+
@VisibleForTesting
SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>();
@VisibleForTesting
SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>();
- // Keep track of recent calls to shouldListenFor*() for debugging.
- private final KeyguardListenQueue mListenModels = new KeyguardListenQueue();
-
private static int sCurrentUser;
public synchronized static void setCurrentUser(int currentUser) {
@@ -464,7 +469,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
@Override
- public void onTrustChanged(boolean enabled, int userId, int flags,
+ public void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags,
List<String> trustGrantedMessages) {
Assert.isMainThread();
boolean wasTrusted = mUserHasTrust.get(userId, false);
@@ -494,7 +499,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- mLogger.logTrustGrantedWithFlags(flags, userId, message);
+ mLogger.logTrustGrantedWithFlags(flags, newlyUnlocked, userId, message);
if (userId == getCurrentUser()) {
final TrustGrantFlags trustGrantFlags = new TrustGrantFlags(flags);
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -502,7 +507,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (cb != null) {
cb.onTrustGrantedForCurrentUser(
shouldDismissKeyguardOnTrustGrantedWithCurrentUser(trustGrantFlags),
- trustGrantFlags, message);
+ newlyUnlocked,
+ trustGrantFlags,
+ message
+ );
}
}
}
@@ -2639,7 +2647,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& !mSecureCameraLaunched;
// Aggregate relevant fields for debug logging.
- maybeLogListenerModelData(
+ logListenerModelData(
new KeyguardActiveUnlockModel(
System.currentTimeMillis(),
user,
@@ -2720,7 +2728,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
&& shouldListenBouncerState && shouldListenUdfpsState
&& shouldListenSideFpsState;
- maybeLogListenerModelData(
+ logListenerModelData(
new KeyguardFingerprintListenModel(
System.currentTimeMillis(),
user,
@@ -2805,7 +2813,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& !mGoingToSleep;
// Aggregate relevant fields for debug logging.
- maybeLogListenerModelData(
+ logListenerModelData(
new KeyguardFaceListenModel(
System.currentTimeMillis(),
user,
@@ -2833,28 +2841,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return shouldListen;
}
- private void maybeLogListenerModelData(@NonNull KeyguardListenModel model) {
+ private void logListenerModelData(@NonNull KeyguardListenModel model) {
mLogger.logKeyguardListenerModel(model);
-
- if (model instanceof KeyguardActiveUnlockModel) {
- mListenModels.add(model);
- return;
- }
-
- // Add model data to the historical buffer.
- final boolean notYetRunning =
- (model instanceof KeyguardFaceListenModel
- && mFaceRunningState != BIOMETRIC_STATE_RUNNING)
- || (model instanceof KeyguardFingerprintListenModel
- && mFingerprintRunningState != BIOMETRIC_STATE_RUNNING);
- final boolean running =
- (model instanceof KeyguardFaceListenModel
- && mFaceRunningState == BIOMETRIC_STATE_RUNNING)
- || (model instanceof KeyguardFingerprintListenModel
- && mFingerprintRunningState == BIOMETRIC_STATE_RUNNING);
- if (notYetRunning && model.getListening()
- || running && !model.getListening()) {
- mListenModels.add(model);
+ if (model instanceof KeyguardFingerprintListenModel) {
+ mFingerprintListenBuffer.insert((KeyguardFingerprintListenModel) model);
+ } else if (model instanceof KeyguardActiveUnlockModel) {
+ mActiveUnlockTriggerBuffer.insert((KeyguardActiveUnlockModel) model);
+ } else if (model instanceof KeyguardFaceListenModel) {
+ mFaceListenBuffer.insert((KeyguardFaceListenModel) model);
}
}
@@ -3929,6 +3923,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
+ mSfpsRequireScreenOnToAuthPrefEnabled);
}
}
+ new DumpsysTableLogger(
+ "KeyguardFingerprintListen",
+ KeyguardFingerprintListenModel.TABLE_HEADERS,
+ mFingerprintListenBuffer.toList()
+ ).printTableData(pw);
}
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
final int userId = mUserTracker.getUserId();
@@ -3954,8 +3953,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched);
pw.println(" mPrimaryBouncerFullyShown=" + mPrimaryBouncerFullyShown);
pw.println(" mNeedsSlowUnlockTransition=" + mNeedsSlowUnlockTransition);
- }
- mListenModels.print(pw);
+ new DumpsysTableLogger(
+ "KeyguardFaceListen",
+ KeyguardFaceListenModel.TABLE_HEADERS,
+ mFaceListenBuffer.toList()
+ ).printTableData(pw);
+ }
+
+ new DumpsysTableLogger(
+ "KeyguardActiveUnlockTriggers",
+ KeyguardActiveUnlockModel.TABLE_HEADERS,
+ mActiveUnlockTriggerBuffer.toList()
+ ).printTableData(pw);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 1d58fc9cf94b..e6b9ac8b38a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -178,11 +178,17 @@ public class KeyguardUpdateMonitorCallback {
* Called after trust was granted.
* @param dismissKeyguard whether the keyguard should be dismissed as a result of the
* trustGranted
+ * @param newlyUnlocked whether the grantedTrust is believed to be the cause of a newly
+ * unlocked device (after being locked).
* @param message optional message the trust agent has provided to show that should indicate
* why trust was granted.
*/
- public void onTrustGrantedForCurrentUser(boolean dismissKeyguard,
- @NonNull TrustGrantFlags flags, @Nullable String message) { }
+ public void onTrustGrantedForCurrentUser(
+ boolean dismissKeyguard,
+ boolean newlyUnlocked,
+ @NonNull TrustGrantFlags flags,
+ @Nullable String message
+ ) { }
/**
* Called when a biometric has been acquired.
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index ceebe4c69091..21d3b24174b6 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -379,14 +379,17 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
fun logTrustGrantedWithFlags(
flags: Int,
+ newlyUnlocked: Boolean,
userId: Int,
message: String?
) {
logBuffer.log(TAG, DEBUG, {
int1 = flags
+ bool1 = newlyUnlocked
int2 = userId
str1 = message
- }, { "trustGrantedWithFlags[user=$int2] flags=${TrustGrantFlags(int1)} message=$str1" })
+ }, { "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " +
+ "flags=${TrustGrantFlags(int1)} message=$str1" })
}
fun logTrustChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
index 4b7e9a57ef6a..98ac2c0bd026 100644
--- a/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
@@ -16,31 +16,25 @@
package com.android.keyguard.mediator
+import android.annotation.BinderThread
import android.os.Trace
-
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ScreenLifecycle
-import com.android.systemui.util.concurrency.Execution
-import com.android.systemui.util.concurrency.PendingTasksContainer
import com.android.systemui.unfold.SysUIUnfoldComponent
+import com.android.systemui.util.concurrency.PendingTasksContainer
import com.android.systemui.util.kotlin.getOrNull
-
import java.util.Optional
-
import javax.inject.Inject
/**
* Coordinates screen on/turning on animations for the KeyguardViewMediator. Specifically for
* screen on events, this will invoke the onDrawn Runnable after all tasks have completed. This
- * should route back to the KeyguardService, which informs the system_server that keyguard has
- * drawn.
+ * should route back to the [com.android.systemui.keyguard.KeyguardService], which informs
+ * the system_server that keyguard has drawn.
*/
@SysUISingleton
class ScreenOnCoordinator @Inject constructor(
- screenLifecycle: ScreenLifecycle,
- unfoldComponent: Optional<SysUIUnfoldComponent>,
- private val execution: Execution
-) : ScreenLifecycle.Observer {
+ unfoldComponent: Optional<SysUIUnfoldComponent>
+) {
private val unfoldLightRevealAnimation = unfoldComponent.map(
SysUIUnfoldComponent::getUnfoldLightRevealOverlayAnimation).getOrNull()
@@ -48,15 +42,12 @@ class ScreenOnCoordinator @Inject constructor(
SysUIUnfoldComponent::getFoldAodAnimationController).getOrNull()
private val pendingTasks = PendingTasksContainer()
- init {
- screenLifecycle.addObserver(this)
- }
-
/**
* When turning on, registers tasks that may need to run before invoking [onDrawn].
+ * This is called on a binder thread from [com.android.systemui.keyguard.KeyguardService].
*/
- override fun onScreenTurningOn(onDrawn: Runnable) {
- execution.assertIsMainThread()
+ @BinderThread
+ fun onScreenTurningOn(onDrawn: Runnable) {
Trace.beginSection("ScreenOnCoordinator#onScreenTurningOn")
pendingTasks.reset()
@@ -68,11 +59,13 @@ class ScreenOnCoordinator @Inject constructor(
Trace.endSection()
}
- override fun onScreenTurnedOn() {
- execution.assertIsMainThread()
-
+ /**
+ * Called when screen is fully turned on and screen on blocker is removed.
+ * This is called on a binder thread from [com.android.systemui.keyguard.KeyguardService].
+ */
+ @BinderThread
+ fun onScreenTurnedOn() {
foldAodAnimationController?.onScreenTurnedOn()
-
pendingTasks.reset()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index d03578531a99..a0f3ecb0634b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -20,9 +20,6 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -74,6 +71,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.CoreStartable;
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
+import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -81,7 +79,6 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.data.repository.BiometricType;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -121,7 +118,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
private final Context mContext;
private final Execution mExecution;
private final CommandQueue mCommandQueue;
- private final StatusBarStateController mStatusBarStateController;
private final ActivityTaskManager mActivityTaskManager;
@Nullable private final FingerprintManager mFingerprintManager;
@Nullable private final FaceManager mFaceManager;
@@ -131,6 +127,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
// TODO: these should be migrated out once ready
@NonNull private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor;
@NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider;
+ @NonNull private final LogContextInteractor mLogContextInteractor;
private final Display mDisplay;
private float mScaleFactor = 1f;
@@ -153,7 +150,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
@Nullable private UdfpsController mUdfpsController;
@Nullable private IUdfpsRefreshRateRequestCallback mUdfpsRefreshRateRequestCallback;
@Nullable private SideFpsController mSideFpsController;
- @Nullable private IBiometricContextListener mBiometricContextListener;
@Nullable private UdfpsLogger mUdfpsLogger;
@VisibleForTesting IBiometricSysuiReceiver mReceiver;
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@@ -728,7 +724,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
@NonNull UdfpsLogger udfpsLogger,
- @NonNull StatusBarStateController statusBarStateController,
+ @NonNull LogContextInteractor logContextInteractor,
@NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull InteractionJankMonitor jankMonitor,
@@ -756,6 +752,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
mFaceEnrolledForUser = new SparseBooleanArray();
mVibratorHelper = vibrator;
+ mLogContextInteractor = logContextInteractor;
mBiometricPromptInteractor = biometricPromptInteractor;
mCredentialViewModelProvider = credentialViewModelProvider;
@@ -770,25 +767,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
});
mWakefulnessLifecycle = wakefulnessLifecycle;
- mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
- @Override
- public void onFinishedWakingUp() {
- notifyDozeChanged(mStatusBarStateController.isDozing(), WAKEFULNESS_AWAKE);
- }
-
- @Override
- public void onStartedGoingToSleep() {
- notifyDozeChanged(mStatusBarStateController.isDozing(), WAKEFULNESS_GOING_TO_SLEEP);
- }
- });
-
- mStatusBarStateController = statusBarStateController;
- mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() {
- @Override
- public void onDozingChanged(boolean isDozing) {
- notifyDozeChanged(isDozing, wakefulnessLifecycle.getWakefulness());
- }
- });
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
int[] faceAuthLocation = context.getResources().getIntArray(
@@ -894,21 +872,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
@Override
public void setBiometricContextListener(IBiometricContextListener listener) {
- mBiometricContextListener = listener;
- notifyDozeChanged(mStatusBarStateController.isDozing(),
- mWakefulnessLifecycle.getWakefulness());
- }
-
- private void notifyDozeChanged(boolean isDozing,
- @WakefulnessLifecycle.Wakefulness int wakefullness) {
- if (mBiometricContextListener != null) {
- try {
- final boolean isAwake = wakefullness == WAKEFULNESS_AWAKE;
- mBiometricContextListener.onDozeChanged(isDozing, isAwake);
- } catch (RemoteException e) {
- Log.w(TAG, "failed to notify initial doze state");
- }
- }
+ mLogContextInteractor.addBiometricContextListener(listener);
}
/**
@@ -1274,7 +1238,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds,
String opPackageName, boolean skipIntro, long operationId, long requestId,
@BiometricMultiSensorMode int multiSensorConfig,
-
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9e38fe49afc6..5990e9ed87de 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -16,8 +16,13 @@
package com.android.systemui.biometrics;
+import static android.app.StatusBarManager.SESSION_BIOMETRIC_PROMPT;
+import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP;
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
+import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
+import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.classifier.Classifier.LOCK_ICON;
@@ -37,6 +42,7 @@ import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
+import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
import android.os.Process;
@@ -76,9 +82,11 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -121,6 +129,10 @@ public class UdfpsController implements DozeReceiver, Dumpable {
// Minimum required delay between consecutive touch logs in milliseconds.
private static final long MIN_TOUCH_LOG_INTERVAL = 50;
+ private static final long MIN_UNCHANGED_INTERACTION_LOG_INTERVAL = 50;
+
+ // This algorithm checks whether the touch is within the sensor's bounding box.
+ private static final int BOUNDING_BOX_TOUCH_CONFIG_ID = 0;
private final Context mContext;
private final Execution mExecution;
@@ -151,6 +163,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
@NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Nullable private final TouchProcessor mTouchProcessor;
+ @NonNull private final SessionTracker mSessionTracker;
@NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
@@ -168,6 +181,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
private int mActivePointerId = -1;
// The timestamp of the most recent touch log.
private long mTouchLogTime;
+ // The timestamp of the most recent log of the UNCHANGED interaction.
+ private long mLastUnchangedInteractionTime;
// 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.
@@ -491,6 +506,63 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
}
+ private int getBiometricSessionType() {
+ if (mOverlay == null) {
+ return -1;
+ }
+ switch (mOverlay.getRequestReason()) {
+ case REASON_AUTH_KEYGUARD:
+ return SESSION_KEYGUARD;
+ case REASON_AUTH_BP:
+ return SESSION_BIOMETRIC_PROMPT;
+ case REASON_ENROLL_FIND_SENSOR:
+ case REASON_ENROLL_ENROLLING:
+ // TODO(b/255634916): create a reason for enrollment (or an "unknown" reason).
+ return SESSION_BIOMETRIC_PROMPT << 1;
+ default:
+ return -1;
+ }
+ }
+
+ private static int toBiometricTouchReportedTouchType(InteractionEvent event) {
+ switch (event) {
+ case DOWN:
+ return SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED__TOUCH_TYPE__TOUCH_TYPE_DOWN;
+ case UP:
+ return SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED__TOUCH_TYPE__TOUCH_TYPE_UP;
+ case CANCEL:
+ return SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED__TOUCH_TYPE__TOUCH_TYPE_CANCEL;
+ default:
+ return SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED__TOUCH_TYPE__TOUCH_TYPE_UNCHANGED;
+ }
+ }
+
+ private void logBiometricTouch(InteractionEvent event, NormalizedTouchData data) {
+ if (event == InteractionEvent.UNCHANGED) {
+ long sinceLastLog = mSystemClock.elapsedRealtime() - mLastUnchangedInteractionTime;
+ if (sinceLastLog < MIN_UNCHANGED_INTERACTION_LOG_INTERVAL) {
+ return;
+ }
+ mLastUnchangedInteractionTime = mSystemClock.elapsedRealtime();
+ }
+
+ final int biometricTouchReportedTouchType = toBiometricTouchReportedTouchType(event);
+ final int sessionId = mSessionTracker.getSessionId(getBiometricSessionType()).getId();
+ final int touchConfigId = BOUNDING_BOX_TOUCH_CONFIG_ID;
+
+ SysUiStatsLog.write(SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED, biometricTouchReportedTouchType,
+ touchConfigId, sessionId, data.getX(), data.getY(), data.getMinor(),
+ data.getMajor(), data.getOrientation(), data.getTime(), data.getGestureStart(),
+ mStatusBarStateController.isDozing());
+
+ if (Build.isDebuggable()) {
+ Log.d(TAG, data.toPrettyString(event.toString()));
+ Log.d(TAG, "sessionId: " + sessionId
+ + ", isAod: " + mStatusBarStateController.isDozing()
+ + ", touchConfigId: " + touchConfigId);
+ }
+ }
+
private boolean newOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
if (!fromUdfpsView) {
Log.e(TAG, "ignoring the touch injected from outside of UdfpsView");
@@ -555,10 +627,10 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
break;
-
default:
break;
}
+ logBiometricTouch(processedTouch.getEvent(), data);
// We should only consume touches that are within the sensor. By returning "false" for
// touches outside of the sensor, we let other UI components consume these events and act on
@@ -754,6 +826,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull @BiometricsBackground Executor biometricsExecutor,
@NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
@NonNull SinglePointerTouchProcessor singlePointerTouchProcessor,
+ @NonNull SessionTracker sessionTracker,
@NonNull AlternateBouncerInteractor alternateBouncerInteractor) {
mContext = context;
mExecution = execution;
@@ -798,6 +871,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
? singlePointerTouchProcessor : null;
+ mSessionTracker = sessionTracker;
mDumpManager.registerDumpable(TAG, this);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 7c0c3b710e66..6f8efba2e84f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -20,6 +20,8 @@ import com.android.systemui.biometrics.data.repository.PromptRepository
import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl
import com.android.systemui.biometrics.domain.interactor.CredentialInteractor
import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.LogContextInteractor
+import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.concurrency.ThreadFactory
import dagger.Binds
@@ -40,6 +42,10 @@ interface BiometricsModule {
@SysUISingleton
fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor
+ @Binds
+ @SysUISingleton
+ fun bindsLogContextInteractor(impl: LogContextInteractorImpl): LogContextInteractor
+
companion object {
/** Background [Executor] for HAL related operations. */
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
new file mode 100644
index 000000000000..3b53eff9dfc5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
@@ -0,0 +1,169 @@
+/*
+ * 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.domain.interactor
+
+import android.hardware.biometrics.IBiometricContextListener
+import android.util.Log
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FoldStateProvider
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
+
+/**
+ * Aggregates UI/device state that is not directly related to biometrics, but is often useful for
+ * logging or optimization purposes (fold state, screen state, etc.)
+ */
+interface LogContextInteractor {
+
+ /** If the device is dozing. */
+ val isDozing: Flow<Boolean>
+
+ /** If the device is currently awake with the screen on. */
+ val isAwake: Flow<Boolean>
+
+ /** Current device fold state, defined as [IBiometricContextListener.FoldState]. */
+ val foldState: Flow<Int>
+
+ /**
+ * Add a permanent context listener.
+ *
+ * Use this method for registering remote context listeners. Use the properties exposed via this
+ * class directly within SysUI.
+ */
+ fun addBiometricContextListener(listener: IBiometricContextListener): Job
+}
+
+@SysUISingleton
+class LogContextInteractorImpl
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val statusBarStateController: StatusBarStateController,
+ private val wakefulnessLifecycle: WakefulnessLifecycle,
+ private val foldProvider: FoldStateProvider,
+) : LogContextInteractor {
+
+ init {
+ foldProvider.start()
+ }
+
+ override val isDozing =
+ conflatedCallbackFlow {
+ val callback =
+ object : StatusBarStateController.StateListener {
+ override fun onDozingChanged(isDozing: Boolean) {
+ trySendWithFailureLogging(isDozing, TAG)
+ }
+ }
+
+ statusBarStateController.addCallback(callback)
+ trySendWithFailureLogging(statusBarStateController.isDozing, TAG)
+ awaitClose { statusBarStateController.removeCallback(callback) }
+ }
+ .distinctUntilChanged()
+
+ override val isAwake =
+ conflatedCallbackFlow {
+ val callback =
+ object : WakefulnessLifecycle.Observer {
+ override fun onFinishedWakingUp() {
+ trySendWithFailureLogging(true, TAG)
+ }
+
+ override fun onStartedGoingToSleep() {
+ trySendWithFailureLogging(false, TAG)
+ }
+ }
+
+ wakefulnessLifecycle.addObserver(callback)
+ trySendWithFailureLogging(wakefulnessLifecycle.isAwake, TAG)
+ awaitClose { wakefulnessLifecycle.removeObserver(callback) }
+ }
+ .distinctUntilChanged()
+
+ override val foldState: Flow<Int> =
+ conflatedCallbackFlow {
+ val callback =
+ object : FoldStateProvider.FoldUpdatesListener {
+ override fun onHingeAngleUpdate(angle: Float) {}
+
+ override fun onFoldUpdate(@FoldStateProvider.FoldUpdate update: Int) {
+ val loggedState =
+ when (update) {
+ FOLD_UPDATE_FINISH_HALF_OPEN ->
+ IBiometricContextListener.FoldState.HALF_OPENED
+ FOLD_UPDATE_FINISH_FULL_OPEN ->
+ IBiometricContextListener.FoldState.FULLY_OPENED
+ FOLD_UPDATE_FINISH_CLOSED ->
+ IBiometricContextListener.FoldState.FULLY_CLOSED
+ else -> null
+ }
+ if (loggedState != null) {
+ trySendWithFailureLogging(loggedState, TAG)
+ }
+ }
+ }
+
+ foldProvider.addCallback(callback)
+ trySendWithFailureLogging(IBiometricContextListener.FoldState.UNKNOWN, TAG)
+ awaitClose { foldProvider.removeCallback(callback) }
+ }
+ .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
+
+ override fun addBiometricContextListener(listener: IBiometricContextListener): Job {
+ return applicationScope.launch {
+ combine(isDozing, isAwake) { doze, awake -> doze to awake }
+ .onEach { (doze, awake) -> listener.onDozeChanged(doze, awake) }
+ .catch { t -> Log.w(TAG, "failed to notify new doze state", t) }
+ .launchIn(this)
+
+ foldState
+ .onEach { state -> listener.onFoldChanged(state) }
+ .catch { t -> Log.w(TAG, "failed to notify new fold state", t) }
+ .launchIn(this)
+
+ listener.asBinder().linkToDeath({ cancel() }, 0)
+ }
+ }
+
+ companion object {
+ private const val TAG = "ContextRepositoryImpl"
+ }
+}
+
+private val WakefulnessLifecycle.isAwake: Boolean
+ get() = wakefulness == WakefulnessLifecycle.WAKEFULNESS_AWAKE
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
index 62bedc627b07..aa60522e512f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
@@ -62,4 +62,19 @@ data class NormalizedTouchData(
nativeSensorBounds.top <= y &&
nativeSensorBounds.bottom >= y
}
+
+ @JvmOverloads
+ fun toPrettyString(tag: String = ""): String =
+ """
+ |NormalizedTouchData [$tag] {
+ | pointerId: $pointerId
+ | x: $x
+ | y: $y
+ | minor: $minor
+ | major: $major
+ | orientation: $orientation
+ | time: $time
+ | gestureStart: $gestureStart
+ |}
+ """.trimMargin()
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 08d29304b548..c0b0571d2c81 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -41,6 +41,7 @@ import com.android.systemui.reardisplay.RearDisplayDialogController
import com.android.systemui.recents.Recents
import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
+import com.android.systemui.statusbar.notification.fsi.FsiChromeRepo
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
import com.android.systemui.stylus.StylusUsiPowerStartable
@@ -80,6 +81,12 @@ abstract class SystemUICoreStartableModule {
@ClassKey(ClipboardListener::class)
abstract fun bindClipboardListener(sysui: ClipboardListener): CoreStartable
+ /** Inject into FsiChromeRepo. */
+ @Binds
+ @IntoMap
+ @ClassKey(FsiChromeRepo::class)
+ abstract fun bindFSIChromeRepo(sysui: FsiChromeRepo): CoreStartable
+
/** Inject into GarbageMonitor.Service. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 1a244cc587c9..ae1160fdc0f1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -165,7 +165,9 @@ object Flags {
/** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */
// TODO(b/256513609): Tracking Bug
- @JvmField val ACTIVE_UNLOCK_CHIPBAR = releasedFlag(217, "active_unlock_chipbar")
+ @JvmField
+ val ACTIVE_UNLOCK_CHIPBAR =
+ resourceBooleanFlag(217, R.bool.flag_active_unlock_chipbar, "active_unlock_chipbar")
/**
* Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the
@@ -401,6 +403,10 @@ object Flags {
// TODO(b/254512756): Tracking Bug
val QUICK_TAP_IN_PCC = releasedFlag(1400, "quick_tap_in_pcc")
+ // TODO(b/261979569): Tracking Bug
+ val QUICK_TAP_FLOW_FRAMEWORK =
+ unreleasedFlag(1401, "quick_tap_flow_framework", teamfood = false)
+
// 1500 - chooser
// TODO(b/254512507): Tracking Bug
val CHOOSER_UNBUNDLED = unreleasedFlag(1500, "chooser_unbundled", teamfood = true)
@@ -455,6 +461,11 @@ object Flags {
// TODO(b/261538825): Tracking Bug
@JvmField
val OUTPUT_SWITCHER_ADVANCED_LAYOUT = unreleasedFlag(2500, "output_switcher_advanced_layout")
+ @JvmField
+ val OUTPUT_SWITCHER_ROUTES_PROCESSING =
+ unreleasedFlag(2501, "output_switcher_routes_processing")
+ @JvmField
+ val OUTPUT_SWITCHER_DEVICE_STATUS = unreleasedFlag(2502, "output_switcher_device_status")
// TODO(b259590361): Tracking bug
val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
index 822b1cfdf877..757afb616fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
@@ -18,11 +18,8 @@ package com.android.systemui.keyguard;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.os.Trace;
-import android.util.Log;
-import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.systemui.dagger.SysUISingleton;
import javax.inject.Inject;
@@ -80,33 +77,10 @@ public class KeyguardLifecyclesDispatcher {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- final Object obj = msg.obj;
switch (msg.what) {
case SCREEN_TURNING_ON:
Trace.beginSection("KeyguardLifecyclesDispatcher#SCREEN_TURNING_ON");
- final String onDrawWaitingTraceTag =
- "Waiting for KeyguardDrawnCallback#onDrawn";
- int traceCookie = System.identityHashCode(msg);
- Trace.beginAsyncSection(onDrawWaitingTraceTag, traceCookie);
- // Ensure the drawn callback is only ever called once
- mScreenLifecycle.dispatchScreenTurningOn(new Runnable() {
- boolean mInvoked;
- @Override
- public void run() {
- if (obj == null) return;
- if (!mInvoked) {
- mInvoked = true;
- try {
- Trace.endAsyncSection(onDrawWaitingTraceTag, traceCookie);
- ((IKeyguardDrawnCallback) obj).onDrawn();
- } catch (RemoteException e) {
- Log.w(TAG, "Exception calling onDrawn():", e);
- }
- } else {
- Log.w(TAG, "KeyguardDrawnCallback#onDrawn() invoked > 1 times");
- }
- }
- });
+ mScreenLifecycle.dispatchScreenTurningOn();
Trace.endSection();
break;
case SCREEN_TURNED_ON:
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index e631816991aa..4a3071dba33e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -77,6 +77,7 @@ import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardService;
import com.android.internal.policy.IKeyguardStateCallback;
+import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.SystemUIApplication;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -91,6 +92,7 @@ public class KeyguardService extends Service {
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
+ private final ScreenOnCoordinator mScreenOnCoordinator;
private final ShellTransitions mShellTransitions;
private static int newModeToLegacyMode(int newMode) {
@@ -254,10 +256,12 @@ public class KeyguardService extends Service {
@Inject
public KeyguardService(KeyguardViewMediator keyguardViewMediator,
KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher,
+ ScreenOnCoordinator screenOnCoordinator,
ShellTransitions shellTransitions) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+ mScreenOnCoordinator = screenOnCoordinator;
mShellTransitions = shellTransitions;
}
@@ -547,6 +551,31 @@ public class KeyguardService extends Service {
checkPermission();
mKeyguardLifecyclesDispatcher.dispatch(KeyguardLifecyclesDispatcher.SCREEN_TURNING_ON,
callback);
+
+ final String onDrawWaitingTraceTag = "Waiting for KeyguardDrawnCallback#onDrawn";
+ final int traceCookie = System.identityHashCode(callback);
+ Trace.beginAsyncSection(onDrawWaitingTraceTag, traceCookie);
+
+ // Ensure the drawn callback is only ever called once
+ mScreenOnCoordinator.onScreenTurningOn(new Runnable() {
+ boolean mInvoked;
+ @Override
+ public void run() {
+ if (callback == null) return;
+ if (!mInvoked) {
+ mInvoked = true;
+ try {
+ Trace.endAsyncSection(onDrawWaitingTraceTag, traceCookie);
+ callback.onDrawn();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Exception calling onDrawn():", e);
+ }
+ } else {
+ Log.w(TAG, "KeyguardDrawnCallback#onDrawn() invoked > 1 times");
+ }
+ }
+ });
+
Trace.endSection();
}
@@ -555,6 +584,7 @@ public class KeyguardService extends Service {
Trace.beginSection("KeyguardService.mBinder#onScreenTurnedOn");
checkPermission();
mKeyguardLifecyclesDispatcher.dispatch(KeyguardLifecyclesDispatcher.SCREEN_TURNED_ON);
+ mScreenOnCoordinator.onScreenTurnedOn();
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 923413633351..9dd41f07b3f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -723,7 +723,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void keyguardDone(boolean strongAuth, int targetUserId) {
- if (targetUserId != mUserTracker.getUserId()) {
+ if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) {
return;
}
if (DEBUG) Log.d(TAG, "keyguardDone");
@@ -746,7 +746,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
public void keyguardDonePending(boolean strongAuth, int targetUserId) {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
if (DEBUG) Log.d(TAG, "keyguardDonePending");
- if (targetUserId != mUserTracker.getUserId()) {
+ if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) {
Trace.endSection();
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
index 0a55294dfe8a..0b04fb43c2d7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -46,7 +46,7 @@ class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenL
listeners.forEach(ScreenListener::onScreenTurningOff)
}
- override fun onScreenTurningOn(ignored: Runnable) {
+ override fun onScreenTurningOn() {
listeners.forEach(ScreenListener::onScreenTurningOn)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
index b3481219a85d..8535eda93f96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
@@ -18,8 +18,6 @@ package com.android.systemui.keyguard;
import android.os.Trace;
-import androidx.annotation.NonNull;
-
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
@@ -50,14 +48,9 @@ public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> impleme
return mScreenState;
}
- /**
- * Dispatch screen turning on events to the registered observers
- *
- * @param onDrawn Invoke to notify the caller that the event has been processed
- */
- public void dispatchScreenTurningOn(@NonNull Runnable onDrawn) {
+ public void dispatchScreenTurningOn() {
setScreenState(SCREEN_TURNING_ON);
- dispatch(Observer::onScreenTurningOn, onDrawn);
+ dispatch(Observer::onScreenTurningOn);
}
public void dispatchScreenTurnedOn() {
@@ -87,12 +80,7 @@ public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> impleme
}
public interface Observer {
- /**
- * Receive the screen turning on event
- *
- * @param onDrawn Invoke to notify the caller that the event has been processed
- */
- default void onScreenTurningOn(@NonNull Runnable onDrawn) {}
+ default void onScreenTurningOn() {}
default void onScreenTurnedOn() {}
default void onScreenTurningOff() {}
default void onScreenTurnedOff() {}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index 73dbeab3030b..ea5b4f43cc75 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -25,6 +25,7 @@ package com.android.systemui.keyguard.data.quickaffordance
object BuiltInKeyguardQuickAffordanceKeys {
// Please keep alphabetical order of const names to simplify future maintenance.
const val CAMERA = "camera"
+ const val DO_NOT_DISTURB = "do_not_disturb"
const val FLASHLIGHT = "flashlight"
const val HOME_CONTROLS = "home"
const val QR_CODE_SCANNER = "qr_code_scanner"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
new file mode 100644
index 000000000000..8efb36624831
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -0,0 +1,190 @@
+/*
+ * 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.quickaffordance
+
+import android.content.Context
+import android.net.Uri
+import android.provider.Settings
+import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+import android.provider.Settings.Global.ZEN_MODE_OFF
+import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
+import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
+import android.service.notification.ZenModeConfig
+import com.android.settingslib.notification.EnableZenModeDialog
+import com.android.settingslib.notification.ZenModeDialogMetricsLogger
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
+import javax.inject.Inject
+
+@SysUISingleton
+class DoNotDisturbQuickAffordanceConfig constructor(
+ private val context: Context,
+ private val controller: ZenModeController,
+ private val secureSettings: SecureSettings,
+ private val userTracker: UserTracker,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val testConditionId: Uri?,
+ testDialog: EnableZenModeDialog?,
+): KeyguardQuickAffordanceConfig {
+
+ @Inject
+ constructor(
+ context: Context,
+ controller: ZenModeController,
+ secureSettings: SecureSettings,
+ userTracker: UserTracker,
+ @Background backgroundDispatcher: CoroutineDispatcher,
+ ) : this(context, controller, secureSettings, userTracker, backgroundDispatcher, null, null)
+
+ private var dndMode: Int = 0
+ private var isAvailable = false
+ private var settingsValue: Int = 0
+
+ private val conditionUri: Uri
+ get() =
+ testConditionId ?: ZenModeConfig.toTimeCondition(
+ context,
+ settingsValue,
+ userTracker.userId,
+ true, /* shortVersion */
+ ).id
+
+ private val dialog: EnableZenModeDialog by lazy {
+ testDialog ?: EnableZenModeDialog(
+ context,
+ R.style.Theme_SystemUI_Dialog,
+ true, /* cancelIsNeutral */
+ ZenModeDialogMetricsLogger(context),
+ )
+ }
+
+ override val key: String = BuiltInKeyguardQuickAffordanceKeys.DO_NOT_DISTURB
+
+ override val pickerName: String = context.getString(R.string.quick_settings_dnd_label)
+
+ override val pickerIconResourceId: Int = R.drawable.ic_do_not_disturb
+
+ override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> = combine(
+ conflatedCallbackFlow {
+ val callback = object: ZenModeController.Callback {
+ override fun onZenChanged(zen: Int) {
+ dndMode = zen
+ trySendWithFailureLogging(updateState(), TAG)
+ }
+
+ override fun onZenAvailableChanged(available: Boolean) {
+ isAvailable = available
+ trySendWithFailureLogging(updateState(), TAG)
+ }
+ }
+
+ dndMode = controller.zen
+ isAvailable = controller.isZenAvailable
+ trySendWithFailureLogging(updateState(), TAG)
+
+ controller.addCallback(callback)
+
+ awaitClose { controller.removeCallback(callback) }
+ },
+ secureSettings
+ .observerFlow(Settings.Secure.ZEN_DURATION)
+ .onStart { emit(Unit) }
+ .map { secureSettings.getInt(Settings.Secure.ZEN_DURATION, ZEN_MODE_OFF) }
+ .flowOn(backgroundDispatcher)
+ .distinctUntilChanged()
+ .onEach { settingsValue = it }
+ ) { callbackFlowValue, _ -> callbackFlowValue }
+
+ override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
+ return if (controller.isZenAvailable) {
+ KeyguardQuickAffordanceConfig.PickerScreenState.Default
+ } else {
+ KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
+ }
+ }
+
+ override fun onTriggered(expandable: Expandable?):
+ KeyguardQuickAffordanceConfig.OnTriggeredResult {
+ return when {
+ !isAvailable ->
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ dndMode != ZEN_MODE_OFF -> {
+ controller.setZen(ZEN_MODE_OFF, null, TAG)
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ }
+ settingsValue == ZEN_DURATION_PROMPT ->
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog(
+ dialog.createDialog(),
+ expandable
+ )
+ settingsValue == ZEN_DURATION_FOREVER -> {
+ controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG)
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ }
+ else -> {
+ controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, conditionUri, TAG)
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ }
+ }
+ }
+
+ private fun updateState(): KeyguardQuickAffordanceConfig.LockScreenState {
+ return if (!isAvailable) {
+ KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+ } else if (dndMode == ZEN_MODE_OFF) {
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_dnd_icon_off,
+ ContentDescription.Resource(R.string.dnd_is_off),
+ ),
+ ActivationState.Inactive,
+ )
+ } else {
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_dnd_icon_on,
+ ContentDescription.Resource(R.string.dnd_is_on),
+ ),
+ ActivationState.Active,
+ )
+ }
+ }
+
+ companion object {
+ const val TAG = "DoNotDisturbQuickAffordanceConfig"
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index 072cfb140e62..71d01ebc8496 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -33,6 +33,7 @@ interface KeyguardDataQuickAffordanceModule {
@Provides
@ElementsIntoSet
fun quickAffordanceConfigs(
+ doNotDisturb: DoNotDisturbQuickAffordanceConfig,
flashlight: FlashlightQuickAffordanceConfig,
home: HomeControlsKeyguardQuickAffordanceConfig,
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
@@ -41,6 +42,7 @@ interface KeyguardDataQuickAffordanceModule {
): Set<KeyguardQuickAffordanceConfig> {
return setOf(
camera,
+ doNotDisturb,
flashlight,
home,
quickAccessWallet,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 98b1a731b82c..02ebcd3200a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.quickaffordance
+import android.app.AlertDialog
import android.content.Intent
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
@@ -141,6 +142,16 @@ interface KeyguardQuickAffordanceConfig {
val intent: Intent,
val canShowWhileLocked: Boolean,
) : OnTriggeredResult()
+
+ /**
+ * Returning this as a result from the [onTriggered] method means that the implementation
+ * has _not_ taken care of the action and the system should show a Dialog using the
+ * given [AlertDialog] and [Expandable].
+ */
+ data class ShowDialog(
+ val dialog: AlertDialog,
+ val expandable: Expandable?,
+ ) : OnTriggeredResult()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 57668c795d1c..8eace76632aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -17,9 +17,11 @@
package com.android.systemui.keyguard.domain.interactor
+import android.app.AlertDialog
import android.content.Intent
import android.util.Log
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
@@ -34,16 +36,17 @@ import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentati
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
-import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
+import javax.inject.Inject
@SysUISingleton
class KeyguardQuickAffordanceInteractor
@@ -57,6 +60,7 @@ constructor(
private val activityStarter: ActivityStarter,
private val featureFlags: FeatureFlags,
private val repository: Lazy<KeyguardQuickAffordanceRepository>,
+ private val launchAnimator: DialogLaunchAnimator,
) {
private val isUsingRepository: Boolean
get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
@@ -131,6 +135,11 @@ constructor(
expandable = expandable,
)
is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> Unit
+ is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog ->
+ showDialog(
+ result.dialog,
+ result.expandable,
+ )
}
}
@@ -279,6 +288,19 @@ constructor(
}
}
+ private fun showDialog(dialog: AlertDialog, expandable: Expandable?) {
+ expandable?.dialogLaunchController()?.let { controller ->
+ SystemUIDialog.applyFlags(dialog)
+ SystemUIDialog.setShowForAllUsers(dialog, true)
+ SystemUIDialog.registerDismissListener(dialog)
+ SystemUIDialog.setDialogSize(dialog)
+ launchAnimator.show(
+ dialog,
+ controller
+ )
+ }
+ }
+
private fun launchQuickAffordance(
intent: Intent,
canShowWhileLocked: Boolean,
@@ -333,9 +355,13 @@ constructor(
fun getPickerFlags(): List<KeyguardPickerFlag> {
return listOf(
KeyguardPickerFlag(
- name = KeyguardQuickAffordanceProviderContract.FlagsTable.FLAG_NAME_FEATURE_ENABLED,
+ name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
value = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
- )
+ ),
+ KeyguardPickerFlag(
+ name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
+ value = featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
+ ),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index 3218f9699f35..5cb7d709a1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -22,7 +22,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
+import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.WakefulnessState
@@ -50,27 +50,22 @@ constructor(
override fun start() {
listenForDraggingUpToBouncer()
- listenForBouncerHiding()
+ listenForBouncer()
}
- private fun listenForBouncerHiding() {
+ private fun listenForBouncer() {
scope.launch {
keyguardInteractor.isBouncerShowing
.sample(
combine(
keyguardInteractor.wakefulnessModel,
keyguardTransitionInteractor.startedKeyguardTransitionStep,
- ) { wakefulnessModel, transitionStep ->
- Pair(wakefulnessModel, transitionStep)
- }
- ) { bouncerShowing, wakefulnessAndTransition ->
- Triple(
- bouncerShowing,
- wakefulnessAndTransition.first,
- wakefulnessAndTransition.second
- )
- }
- .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) ->
+ ::Pair
+ ),
+ ::toTriple
+ )
+ .collect { triple ->
+ val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
if (
!isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
) {
@@ -91,7 +86,19 @@ constructor(
animator = getAnimator(),
)
)
+ } else if (
+ isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN
+ ) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = name,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.BOUNCER,
+ animator = getAnimator(),
+ )
+ )
}
+ Unit
}
}
}
@@ -104,24 +111,20 @@ constructor(
combine(
keyguardTransitionInteractor.finishedKeyguardState,
keyguardInteractor.statusBarState,
- ) { finishedKeyguardState, statusBarState ->
- Pair(finishedKeyguardState, statusBarState)
- }
- ) { shadeModel, keyguardStateAndStatusBarState ->
- Triple(
- shadeModel,
- keyguardStateAndStatusBarState.first,
- keyguardStateAndStatusBarState.second
- )
- }
- .collect { (shadeModel, keyguardState, statusBarState) ->
+ ::Pair
+ ),
+ ::toTriple
+ )
+ .collect { triple ->
+ val (shadeModel, keyguardState, statusBarState) = triple
+
val id = transitionId
if (id != null) {
// An existing `id` means a transition is started, and calls to
// `updateTransition` will control it until FINISHED
keyguardTransitionRepository.updateTransition(
id,
- shadeModel.expansionAmount,
+ 1f - shadeModel.expansionAmount,
if (
shadeModel.expansionAmount == 0f || shadeModel.expansionAmount == 1f
) {
@@ -137,7 +140,7 @@ constructor(
if (
keyguardState == KeyguardState.LOCKSCREEN &&
shadeModel.isUserDragging &&
- statusBarState != SHADE_LOCKED
+ statusBarState == KEYGUARD
) {
transitionId =
keyguardTransitionRepository.startTransition(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 2cf5fb98d07e..2a3a33eff274 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -77,7 +77,6 @@ constructor(
/** Runnable to show the primary bouncer. */
val showRunnable = Runnable {
- repository.setPrimaryVisible(true)
repository.setPrimaryShow(
KeyguardBouncerModel(
promptReason = repository.bouncerPromptReason ?: 0,
@@ -86,6 +85,7 @@ constructor(
)
)
repository.setPrimaryShowingSoon(false)
+ repository.setPrimaryVisible(true)
primaryBouncerCallbackInteractor.dispatchVisibilityChanged(View.VISIBLE)
}
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
index f772b17a7fb6..3d5985c5c7aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -105,14 +105,6 @@ object KeyguardBouncerViewBinder {
}
launch {
- viewModel.showWithFullExpansion.collect { model ->
- hostViewController.resetSecurityContainer()
- hostViewController.showPromptReason(model.promptReason)
- hostViewController.onResume()
- }
- }
-
- launch {
viewModel.hide.collect {
hostViewController.cancelDismissAction()
hostViewController.cleanUp()
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
index e5d4e4971baa..737c35d866c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -22,10 +22,8 @@ import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
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.map
/** Models UI state for the lock screen bouncer; handles user input. */
@@ -44,10 +42,6 @@ constructor(
/** Observe whether bouncer is showing. */
val show: Flow<KeyguardBouncerModel> = interactor.show
- /** 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
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 a7d60a125bee..f2e8e42cffa2 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -123,7 +123,7 @@ public class LogModule {
@SysUISingleton
@QSLog
public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) {
- return factory.create("QSLog", 500 /* maxSize */, false /* systrace */);
+ return factory.create("QSLog", 700 /* maxSize */, false /* systrace */);
}
/** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5202562861ea..ea81c083abca 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -847,6 +847,11 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
refreshLayout(ld);
}
repositionNavigationBar(rotation);
+ // NOTE(b/260220098): In some cases, the recreated nav bar will already have the right
+ // configuration, which means that NavBarView will not receive a configuration change to
+ // propagate to EdgeBackGestureHandler (which is injected into this and NBV). As such, we
+ // should also force-update the gesture handler to ensure it updates to the right bounds
+ mEdgeBackGestureHandler.onConfigurationChanged(newConfig);
if (canShowSecondaryHandle()) {
if (rotation != mCurrentRotation) {
mCurrentRotation = rotation;
@@ -1305,8 +1310,10 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
private void onVerticalChanged(boolean isVertical) {
- mCentralSurfacesOptionalLazy.get().ifPresent(statusBar ->
- statusBar.getNotificationPanelViewController().setQsScrimEnabled(!isVertical));
+ Optional<CentralSurfaces> cs = mCentralSurfacesOptionalLazy.get();
+ if (cs.isPresent() && cs.get().getNotificationPanelViewController() != null) {
+ cs.get().getNotificationPanelViewController().setQsScrimEnabled(!isVertical);
+ }
}
private boolean onNavigationTouch(View v, MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 8ceee1a950ea..7523d6e976ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -11,7 +11,6 @@ import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -31,6 +30,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
+import com.android.systemui.qs.logging.QSLogger;
import java.util.ArrayList;
import java.util.List;
@@ -38,11 +38,9 @@ import java.util.Set;
public class PagedTileLayout extends ViewPager implements QSTileLayout {
- private static final boolean DEBUG = false;
private static final String CURRENT_PAGE = "current_page";
private static final int NO_PAGE = -1;
- private static final String TAG = "PagedTileLayout";
private static final int REVEAL_SCROLL_DURATION_MILLIS = 750;
private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
private static final long BOUNCE_ANIMATION_DURATION = 450L;
@@ -55,6 +53,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private final ArrayList<TileRecord> mTiles = new ArrayList<>();
private final ArrayList<TileLayout> mPages = new ArrayList<>();
+ private QSLogger mLogger;
@Nullable
private PageIndicator mPageIndicator;
private float mPageIndicatorPosition;
@@ -146,9 +145,15 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
if (mLayoutOrientation != newConfig.orientation) {
mLayoutOrientation = newConfig.orientation;
- mDistributeTiles = true;
+ forceTilesRedistribution("orientation changed to " + mLayoutOrientation);
setCurrentItem(0, false);
mPageToRestore = 0;
+ } else {
+ // logging in case we missed redistribution because orientation was not changed
+ // while configuration changed, can be removed after b/255208946 is fixed
+ mLogger.d(
+ "Orientation didn't change, tiles might be not redistributed, new config",
+ newConfig);
}
}
@@ -226,7 +231,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
// Keep on drawing until the animation has finished.
postInvalidateOnAnimation();
} catch (NullPointerException e) {
- Log.e(TAG, "FakeDragBy called before begin", e);
+ mLogger.logException("FakeDragBy called before begin", e);
// If we were trying to fake drag, it means we just added a new tile to the last
// page, so animate there.
final int lastPageNumber = mPages.size() - 1;
@@ -246,7 +251,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
super.endFakeDrag();
} catch (NullPointerException e) {
// Not sure what's going on. Let's log it
- Log.e(TAG, "endFakeDrag called without velocityTracker", e);
+ mLogger.logException("endFakeDrag called without velocityTracker", e);
}
}
@@ -304,14 +309,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
@Override
public void addTile(TileRecord tile) {
mTiles.add(tile);
- mDistributeTiles = true;
+ forceTilesRedistribution("adding new tile");
requestLayout();
}
@Override
public void removeTile(TileRecord tile) {
if (mTiles.remove(tile)) {
- mDistributeTiles = true;
+ forceTilesRedistribution("removing tile");
requestLayout();
}
}
@@ -367,19 +372,11 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
final int tilesPerPageCount = mPages.get(0).maxTiles();
int index = 0;
final int totalTilesCount = mTiles.size();
- if (DEBUG) {
- Log.d(TAG, "Distributing tiles: "
- + "[tilesPerPageCount=" + tilesPerPageCount + "]"
- + "[totalTilesCount=" + totalTilesCount + "]"
- );
- }
+ mLogger.logTileDistributionInProgress(tilesPerPageCount, totalTilesCount);
for (int i = 0; i < totalTilesCount; i++) {
TileRecord tile = mTiles.get(i);
if (mPages.get(index).mRecords.size() == tilesPerPageCount) index++;
- if (DEBUG) {
- Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
- + index);
- }
+ mLogger.logTileDistributed(tile.tile.getClass().getSimpleName(), index);
mPages.get(index).addTile(tile);
}
}
@@ -394,11 +391,11 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
return;
}
while (mPages.size() < numPages) {
- if (DEBUG) Log.d(TAG, "Adding page");
+ mLogger.d("Adding new page");
mPages.add(createTileLayout());
}
while (mPages.size() > numPages) {
- if (DEBUG) Log.d(TAG, "Removing page");
+ mLogger.d("Removing page");
mPages.remove(mPages.size() - 1);
}
mPageIndicator.setNumPages(mPages.size());
@@ -417,8 +414,12 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
changed |= mPages.get(i).updateResources();
}
if (changed) {
- mDistributeTiles = true;
+ forceTilesRedistribution("resources in pages changed");
requestLayout();
+ } else {
+ // logging in case we missed redistribution because number of column in updateResources
+ // was not changed, can be removed after b/255208946 is fixed
+ mLogger.d("resource in pages didn't change, tiles might be not redistributed");
}
return changed;
}
@@ -430,7 +431,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
for (int i = 0; i < mPages.size(); i++) {
if (mPages.get(i).setMinRows(minRows)) {
changed = true;
- mDistributeTiles = true;
+ forceTilesRedistribution("minRows changed in page");
}
}
return changed;
@@ -443,7 +444,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
for (int i = 0; i < mPages.size(); i++) {
if (mPages.get(i).setMaxColumns(maxColumns)) {
changed = true;
- mDistributeTiles = true;
+ forceTilesRedistribution("maxColumns in pages changed");
}
}
return changed;
@@ -710,14 +711,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private final PagerAdapter mAdapter = new PagerAdapter() {
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
- if (DEBUG) Log.d(TAG, "Destantiating " + position);
+ mLogger.d("Destantiating page at", position);
container.removeView((View) object);
updateListening();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
- if (DEBUG) Log.d(TAG, "Instantiating " + position);
+ mLogger.d("Instantiating page at", position);
if (isLayoutRtl()) {
position = mPages.size() - 1 - position;
}
@@ -745,10 +746,15 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
* Force all tiles to be redistributed across pages.
* Should be called when one of the following changes: rows, columns, number of tiles.
*/
- public void forceTilesRedistribution() {
+ public void forceTilesRedistribution(String reason) {
+ mLogger.d("forcing tile redistribution across pages, reason", reason);
mDistributeTiles = true;
}
+ public void setLogger(QSLogger qsLogger) {
+ mLogger = qsLogger;
+ }
+
public interface PageListener {
int INVALID_PAGE = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 6517ff33a49d..7067c220da87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -43,6 +43,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.RemeasuringLinearLayout;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -106,6 +107,7 @@ public class QSPanel extends LinearLayout implements Tunable {
private ViewGroup mMediaHostView;
private boolean mShouldMoveMediaOnExpansion = true;
private boolean mUsingCombinedHeaders = false;
+ private QSLogger mQsLogger;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -122,7 +124,8 @@ public class QSPanel extends LinearLayout implements Tunable {
}
- void initialize() {
+ void initialize(QSLogger qsLogger) {
+ mQsLogger = qsLogger;
mTileLayout = getOrCreateTileLayout();
if (mUsingMediaPlayer) {
@@ -206,6 +209,7 @@ public class QSPanel extends LinearLayout implements Tunable {
if (mTileLayout == null) {
mTileLayout = (QSTileLayout) LayoutInflater.from(mContext)
.inflate(R.layout.qs_paged_tile_layout, this, false);
+ mTileLayout.setLogger(mQsLogger);
mTileLayout.setSquishinessFraction(mSquishinessFraction);
}
return mTileLayout;
@@ -735,6 +739,8 @@ public class QSPanel extends LinearLayout implements Tunable {
default void setExpansion(float expansion, float proposedTranslation) {}
int getNumVisibleTiles();
+
+ default void setLogger(QSLogger qsLogger) { }
}
interface OnConfigurationChangedListener {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index b2ca6b728113..cabe1daf16f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -122,9 +122,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
}
switchTileLayout(true);
mBrightnessMirrorHandler.onQsPanelAttached();
-
- ((PagedTileLayout) mView.getOrCreateTileLayout())
- .setOnTouchListener(mTileLayoutTouchListener);
+ PagedTileLayout pagedTileLayout= ((PagedTileLayout) mView.getOrCreateTileLayout());
+ pagedTileLayout.setOnTouchListener(mTileLayoutTouchListener);
}
@Override
@@ -150,7 +149,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
@Override
protected void onSplitShadeChanged() {
- ((PagedTileLayout) mView.getOrCreateTileLayout()).forceTilesRedistribution();
+ ((PagedTileLayout) mView.getOrCreateTileLayout())
+ .forceTilesRedistribution("Split shade state changed");
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 60d2c177c7cd..7bb672ce5880 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -70,7 +70,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
protected final MediaHost mMediaHost;
protected final MetricsLogger mMetricsLogger;
private final UiEventLogger mUiEventLogger;
- private final QSLogger mQSLogger;
+ protected final QSLogger mQSLogger;
private final DumpManager mDumpManager;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
protected boolean mShouldUseSplitNotificationShade;
@@ -152,7 +152,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
@Override
protected void onInit() {
- mView.initialize();
+ mView.initialize(mQSLogger);
mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
}
@@ -430,6 +430,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
pw.println(" horizontal layout: " + mUsingHorizontalLayout);
pw.println(" last orientation: " + mLastOrientation);
}
+ pw.println(" mShouldUseSplitNotificationShade: " + mShouldUseSplitNotificationShade);
}
public QSPanel.QSTileLayout getTileLayout() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 9f6317fd931b..d682853f5fef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -21,10 +21,13 @@ import com.android.systemui.log.dagger.QSLog
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel
import com.android.systemui.plugins.log.LogLevel.DEBUG
+import com.android.systemui.plugins.log.LogLevel.ERROR
import com.android.systemui.plugins.log.LogLevel.VERBOSE
+import com.android.systemui.plugins.log.LogLevel.WARNING
import com.android.systemui.plugins.log.LogMessage
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.statusbar.StatusBarState
+import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
private const val TAG = "QSLog"
@@ -33,6 +36,26 @@ class QSLogger @Inject constructor(
@QSLog private val buffer: LogBuffer
) {
+ fun d(@CompileTimeConstant msg: String) = buffer.log(TAG, DEBUG, msg)
+
+ fun e(@CompileTimeConstant msg: String) = buffer.log(TAG, ERROR, msg)
+
+ fun v(@CompileTimeConstant msg: String) = buffer.log(TAG, VERBOSE, msg)
+
+ fun w(@CompileTimeConstant msg: String) = buffer.log(TAG, WARNING, msg)
+
+ fun logException(@CompileTimeConstant logMsg: String, ex: Exception) {
+ buffer.log(TAG, ERROR, {}, { logMsg }, exception = ex)
+ }
+
+ fun v(@CompileTimeConstant msg: String, arg: Any) {
+ buffer.log(TAG, VERBOSE, { str1 = arg.toString() }, { "$msg: $str1" })
+ }
+
+ fun d(@CompileTimeConstant msg: String, arg: Any) {
+ buffer.log(TAG, DEBUG, { str1 = arg.toString() }, { "$msg: $str1" })
+ }
+
fun logTileAdded(tileSpec: String) {
log(DEBUG, {
str1 = tileSpec
@@ -236,6 +259,24 @@ class QSLogger @Inject constructor(
})
}
+ fun logTileDistributionInProgress(tilesPerPageCount: Int, totalTilesCount: Int) {
+ log(DEBUG, {
+ int1 = tilesPerPageCount
+ int2 = totalTilesCount
+ }, {
+ "Distributing tiles: [tilesPerPageCount=$int1] [totalTilesCount=$int2]"
+ })
+ }
+
+ fun logTileDistributed(tileName: String, pageIndex: Int) {
+ log(DEBUG, {
+ str1 = tileName
+ int1 = pageIndex
+ }, {
+ "Adding $str1 to page number $int1"
+ })
+ }
+
private fun toStateString(state: Int): String {
return when (state) {
Tile.STATE_ACTIVE -> "active"
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 00d129ae70ca..4d005bebd99e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -889,7 +889,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
* Notifies the Launcher that screen is starting to turn on.
*/
@Override
- public void onScreenTurningOn(@NonNull Runnable ignored) {
+ public void onScreenTurningOn() {
try {
if (mOverviewProxy != null) {
mOverviewProxy.onScreenTurningOn();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 61e53da36a2a..eabd2763656d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4998,10 +4998,37 @@ public final class NotificationPanelViewController implements Dumpable {
return mExpandedFraction;
}
+ /**
+ * This method should not be used anymore, you should probably use {@link #isShadeFullyOpen()}
+ * instead. It was overused as indicating if shade is open or we're on keyguard/AOD.
+ * Moving forward we should be explicit about the what state we're checking.
+ * @return if panel is covering the screen, which means we're in expanded shade or keyguard/AOD
+ *
+ * @deprecated depends on the state you check, use {@link #isShadeFullyOpen()},
+ * {@link #isOnAod()}, {@link #isOnKeyguard()} instead.
+ */
+ @Deprecated
public boolean isFullyExpanded() {
return mExpandedHeight >= getMaxPanelTransitionDistance();
}
+ /**
+ * Returns true if shade is fully opened, that is we're actually in the notification shade
+ * with QQS or QS. It's different from {@link #isFullyExpanded()} that it will not report
+ * shade as always expanded if we're on keyguard/AOD. It will return true only when user goes
+ * from keyguard to shade.
+ */
+ public boolean isShadeFullyOpen() {
+ if (mBarState == SHADE) {
+ return isFullyExpanded();
+ } else if (mBarState == SHADE_LOCKED) {
+ return true;
+ } else {
+ // case of two finger swipe from the top of keyguard
+ return computeQsExpansionFraction() == 1;
+ }
+ }
+
public boolean isFullyCollapsed() {
return mExpandedFraction <= 0.0f;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index a41a15d4e2a2..ad5a68e4dc3f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -63,8 +63,14 @@ public interface ShadeController {
*/
boolean closeShadeIfOpen();
- /** Returns whether the shade is currently open or opening. */
- boolean isShadeOpen();
+ /**
+ * Returns whether the shade is currently open.
+ * Even though in the current implementation shade is in expanded state on keyguard, this
+ * method makes distinction between shade being truly open and plain keyguard state:
+ * - if QS and notifications are visible on the screen, return true
+ * - for any other state, including keyguard, return false
+ */
+ boolean isShadeFullyOpen();
/**
* Add a runnable for NotificationPanelView to post when the panel is expanded.
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index 638e74883f23..026673adb86b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -154,9 +154,8 @@ public final class ShadeControllerImpl implements ShadeController {
}
@Override
- public boolean isShadeOpen() {
- return mNotificationPanelViewController.isExpanding()
- || mNotificationPanelViewController.isFullyExpanded();
+ public boolean isShadeFullyOpen() {
+ return mNotificationPanelViewController.isShadeFullyOpen();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java
index 959c339ab3e5..f87a1ed57d15 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java
@@ -16,14 +16,17 @@
package com.android.systemui.shade;
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shade.data.repository.ShadeRepository;
+import com.android.systemui.shade.data.repository.ShadeRepositoryImpl;
import dagger.Binds;
import dagger.Module;
-/** Provides a {@link ShadeStateEvents} in {@link SysUISingleton} scope. */
+/** Provides Shade-related events and information. */
@Module
public abstract class ShadeEventsModule {
@Binds
abstract ShadeStateEvents bindShadeEvents(ShadeExpansionStateManager impl);
+
+ @Binds abstract ShadeRepository shadeRepository(ShadeRepositoryImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 09019a69df47..bf7a2be2e4ca 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -27,11 +27,17 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
+interface ShadeRepository {
+ /** ShadeModel information regarding shade expansion events */
+ val shadeModel: Flow<ShadeModel>
+}
+
/** Business logic for shade interactions */
@SysUISingleton
-class ShadeRepository @Inject constructor(shadeExpansionStateManager: ShadeExpansionStateManager) {
-
- val shadeModel: Flow<ShadeModel> =
+class ShadeRepositoryImpl
+@Inject
+constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepository {
+ override val shadeModel: Flow<ShadeModel> =
conflatedCallbackFlow {
val callback =
object : ShadeExpansionListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index c1e398a404c6..8ceb46e8557c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -946,7 +946,8 @@ public class KeyguardIndicationController {
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
if (mAlternateBouncerInteractor.isVisibleState()) {
return; // udfps affordance is highlighted, no need to show action to unlock
- } else if (mKeyguardUpdateMonitor.isFaceEnrolled()) {
+ } else if (!mKeyguardUpdateMonitor.getIsFaceAuthenticated()
+ && mKeyguardUpdateMonitor.isFaceEnrolled()) {
String message = mContext.getString(R.string.keyguard_retry);
mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
}
@@ -1209,8 +1210,12 @@ public class KeyguardIndicationController {
}
@Override
- public void onTrustGrantedForCurrentUser(boolean dismissKeyguard,
- @NonNull TrustGrantFlags flags, @Nullable String message) {
+ public void onTrustGrantedForCurrentUser(
+ boolean dismissKeyguard,
+ boolean newlyUnlocked,
+ @NonNull TrustGrantFlags flags,
+ @Nullable String message
+ ) {
showTrustGrantedMessage(dismissKeyguard, message);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 0eb00008e289..dc9b41690d61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -306,9 +306,12 @@ interface Roundable {
*/
class RoundableState(
internal val targetView: View,
- roundable: Roundable,
- internal val maxRadius: Float,
+ private val roundable: Roundable,
+ maxRadius: Float,
) {
+ internal var maxRadius = maxRadius
+ private set
+
/** Animatable for top roundness */
private val topAnimatable = topAnimatable(roundable)
@@ -356,6 +359,13 @@ class RoundableState(
PropertyAnimator.setProperty(targetView, bottomAnimatable, value, DURATION, animated)
}
+ fun setMaxRadius(radius: Float) {
+ if (maxRadius != radius) {
+ maxRadius = radius
+ roundable.applyRoundnessAndInvalidate()
+ }
+ }
+
fun debugString() = buildString {
append("TargetView: ${targetView.hashCode()} ")
append("Top: $topRoundness ")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt
new file mode 100644
index 000000000000..b48322822c86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt
@@ -0,0 +1,102 @@
+package com.android.systemui.statusbar.notification.fsi
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import android.os.RemoteException
+import android.service.dreams.IDreamManager
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider
+import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Class that bridges the gap between clean app architecture and existing code. Provides new
+ * implementation of StatusBarNotificationActivityStarter launchFullscreenIntent that pipes
+ * one-directional data => FsiChromeViewModel => FsiChromeView.
+ */
+@SysUISingleton
+class FsiChromeRepo
+@Inject
+constructor(
+ private val context: Context,
+ private val pm: PackageManager,
+ private val keyguardRepo: KeyguardRepository,
+ private val launchFullScreenIntentProvider: LaunchFullScreenIntentProvider,
+ private val featureFlags: FeatureFlags,
+ private val uiBgExecutor: Executor,
+ private val dreamManager: IDreamManager,
+ private val centralSurfaces: CentralSurfaces
+) : CoreStartable {
+
+ companion object {
+ private const val classTag = "FsiChromeRepo"
+ }
+
+ data class FSIInfo(
+ val appName: String,
+ val appIcon: Drawable,
+ val fullscreenIntent: PendingIntent
+ )
+
+ private val _infoFlow = MutableStateFlow<FSIInfo?>(null)
+ val infoFlow: StateFlow<FSIInfo?> = _infoFlow
+
+ override fun start() {
+ log("$classTag start listening for FSI notifications")
+
+ // Listen for FSI launch events for the lifetime of SystemUI.
+ launchFullScreenIntentProvider.registerListener { entry -> launchFullscreenIntent(entry) }
+ }
+
+ fun dismiss() {
+ _infoFlow.value = null
+ }
+
+ fun onFullscreen() {
+ // TODO(b/243421660) implement transition from container to fullscreen
+ }
+
+ fun stopScreenSaver() {
+ uiBgExecutor.execute {
+ try {
+ dreamManager.awaken()
+ } catch (e: RemoteException) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+ fun launchFullscreenIntent(entry: NotificationEntry) {
+ if (!featureFlags.isEnabled(Flags.FSI_CHROME)) {
+ return
+ }
+ if (!keyguardRepo.isKeyguardShowing()) {
+ return
+ }
+ stopScreenSaver()
+
+ var appName = pm.getApplicationLabel(context.applicationInfo) as String
+ val appIcon = pm.getApplicationIcon(context.packageName)
+ val fullscreenIntent = entry.sbn.notification.fullScreenIntent
+
+ log("FsiChromeRepo launchFullscreenIntent appName=$appName appIcon $appIcon")
+ _infoFlow.value = FSIInfo(appName, appIcon, fullscreenIntent)
+
+ // If screen is off or we're showing AOD, show lockscreen.
+ centralSurfaces.wakeUpForFullScreenIntent()
+
+ // Don't show HUN since we're already showing FSI.
+ entry.notifyFullScreenIntentLaunched()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 0213b969551e..20412452b7f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -214,7 +214,11 @@ public abstract class ExpandableOutlineView extends ExpandableView {
} else {
maxRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius);
}
- mRoundableState = new RoundableState(this, this, maxRadius);
+ if (mRoundableState == null) {
+ mRoundableState = new RoundableState(this, this, maxRadius);
+ } else {
+ mRoundableState.setMaxRadius(maxRadius);
+ }
setClipToOutline(mAlwaysRoundBothCorners);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index d22bfe8b9e3c..5db95d77d135 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3707,7 +3707,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* of DisplayArea into relative coordinates for all windows, we need to correct the
* QS Head bounds here.
*/
- final int xOffset = Math.round(ev.getRawX() - ev.getX());
+ final int xOffset = Math.round(ev.getRawX() - ev.getX() + mQsHeader.getLeft());
final int yOffset = Math.round(ev.getRawY() - ev.getY());
mQsHeaderBound.offsetTo(xOffset, yOffset);
return mQsHeaderBound.contains((int) ev.getRawX(), (int) ev.getRawY());
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 65946c59d661..c1ed10c2afcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -35,6 +35,7 @@ import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -1135,10 +1136,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private void onFoldedStateChangedInternal(boolean isFolded, boolean willGoToSleep) {
// Folded state changes are followed by a screen off event.
// By default turning off the screen also closes the shade.
- // We want to make sure that the shade status is kept after
- // folding/unfolding.
- boolean isShadeOpen = mShadeController.isShadeOpen();
- boolean leaveOpen = isShadeOpen && !willGoToSleep;
+ // We want to make sure that the shade status is kept after folding/unfolding.
+ boolean isShadeOpen = mShadeController.isShadeFullyOpen();
+ boolean leaveOpen = isShadeOpen && !willGoToSleep && mState == SHADE;
if (DEBUG) {
Log.d(TAG, String.format(
"#onFoldedStateChanged(): "
@@ -1149,18 +1149,17 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
isFolded, willGoToSleep, isShadeOpen, leaveOpen));
}
if (leaveOpen) {
- if (mKeyguardStateController.isShowing()) {
- // When device state changes on keyguard we don't want to keep the state of
- // the shade and instead we open clean state of keyguard with shade closed.
- // Normally some parts of QS state (like expanded/collapsed) are persisted and
- // that causes incorrect UI rendering, especially when changing state with QS
- // expanded. To prevent that we can close QS which resets QS and some parts of
- // the shade to its default state. Read more in b/201537421
- mCloseQsBeforeScreenOff = true;
- } else {
- // below makes shade stay open when going from folded to unfolded
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
- }
+ // below makes shade stay open when going from folded to unfolded
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ }
+ if (mState != SHADE && isShadeOpen) {
+ // When device state changes on KEYGUARD/SHADE_LOCKED we don't want to keep the state of
+ // the shade and instead we open clean state of keyguard with shade closed.
+ // Normally some parts of QS state (like expanded/collapsed) are persisted and
+ // that causes incorrect UI rendering, especially when changing state with QS
+ // expanded. To prevent that we can close QS which resets QS and some parts of
+ // the shade to its default state. Read more in b/201537421
+ mCloseQsBeforeScreenOff = true;
}
}
@@ -3559,7 +3558,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
- public void onScreenTurningOn(Runnable onDrawn) {
+ public void onScreenTurningOn() {
mFalsingCollector.onScreenTurningOn();
mNotificationPanelViewController.onScreenTurningOn();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 05bf8604c2c0..be6e0cc8a996 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -50,6 +50,8 @@ import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.EventLogTags;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
@@ -106,6 +108,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
private final LockPatternUtils mLockPatternUtils;
private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback;
private final ActivityIntentHelper mActivityIntentHelper;
+ private final FeatureFlags mFeatureFlags;
private final MetricsLogger mMetricsLogger;
private final StatusBarNotificationActivityStarterLogger mLogger;
@@ -149,7 +152,8 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
NotificationPanelViewController panel,
ActivityLaunchAnimator activityLaunchAnimator,
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider,
- LaunchFullScreenIntentProvider launchFullScreenIntentProvider) {
+ LaunchFullScreenIntentProvider launchFullScreenIntentProvider,
+ FeatureFlags featureFlags) {
mContext = context;
mMainThreadHandler = mainThreadHandler;
mUiBgExecutor = uiBgExecutor;
@@ -170,6 +174,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
mLockPatternUtils = lockPatternUtils;
mStatusBarRemoteInputCallback = remoteInputCallback;
mActivityIntentHelper = activityIntentHelper;
+ mFeatureFlags = featureFlags;
mMetricsLogger = metricsLogger;
mLogger = logger;
mOnUserInteractionCallback = onUserInteractionCallback;
@@ -548,7 +553,10 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
mLogger.logFullScreenIntentSuppressedByVR(entry);
return;
}
-
+ if (mFeatureFlags.isEnabled(Flags.FSI_CHROME)) {
+ // FsiChromeRepo runs its own implementation of launchFullScreenIntent
+ return;
+ }
// Stop screensaver if the notification has a fullscreen intent.
// (like an incoming phone call)
mUiBgExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 6216acd6081e..101bd4483cb3 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.unfold
+import android.annotation.BinderThread
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.os.PowerManager
@@ -41,7 +42,6 @@ import java.util.function.Consumer
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
/**
@@ -52,7 +52,7 @@ import kotlinx.coroutines.launch
class FoldAodAnimationController
@Inject
constructor(
- @Main private val executor: DelayableExecutor,
+ @Main private val mainExecutor: DelayableExecutor,
private val context: Context,
private val deviceStateManager: DeviceStateManager,
private val wakefulnessLifecycle: WakefulnessLifecycle,
@@ -89,7 +89,7 @@ constructor(
override fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
this.centralSurfaces = centralSurfaces
- deviceStateManager.registerCallback(executor, FoldListener())
+ deviceStateManager.registerCallback(mainExecutor, FoldListener())
wakefulnessLifecycle.addObserver(this)
// TODO(b/254878364): remove this call to NPVC.getView()
@@ -139,7 +139,8 @@ constructor(
* @param onReady callback when the animation is ready
* @see [com.android.systemui.keyguard.KeyguardViewMediator]
*/
- fun onScreenTurningOn(onReady: Runnable) {
+ @BinderThread
+ fun onScreenTurningOn(onReady: Runnable) = mainExecutor.execute {
if (shouldPlayAnimation) {
// The device was not dozing and going to sleep after folding, play the animation
@@ -179,12 +180,13 @@ constructor(
}
}
- fun onScreenTurnedOn() {
+ @BinderThread
+ fun onScreenTurnedOn() = mainExecutor.execute {
if (shouldPlayAnimation) {
cancelAnimation?.run()
// Post starting the animation to the next frame to avoid junk due to inset changes
- cancelAnimation = executor.executeDelayed(startAnimationRunnable, /* delayMillis= */ 0)
+ cancelAnimation = mainExecutor.executeDelayed(startAnimationRunnable, /* delayMillis= */ 0)
shouldPlayAnimation = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index b2ec27c8ce43..9cca95028729 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.unfold
+import android.annotation.BinderThread
import android.content.ContentResolver
import android.content.Context
import android.graphics.PixelFormat
@@ -34,9 +35,7 @@ import android.view.SurfaceControlViewHost
import android.view.SurfaceSession
import android.view.WindowManager
import android.view.WindowlessWindowManager
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.LinearLightRevealEffect
@@ -45,7 +44,7 @@ import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation.AddOverlayR
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
-import com.android.systemui.util.Assert.isMainThread
+import com.android.systemui.util.concurrency.ThreadFactory
import com.android.systemui.util.traceSection
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import java.util.Optional
@@ -64,14 +63,16 @@ constructor(
private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
private val displayAreaHelper: Optional<DisplayAreaHelper>,
@Main private val executor: Executor,
- @UiBackground private val backgroundExecutor: Executor,
- @Background private val bgHandler: Handler,
+ private val threadFactory: ThreadFactory,
private val rotationChangeProvider: RotationChangeProvider,
) {
private val transitionListener = TransitionListener()
private val rotationWatcher = RotationWatcher()
+ private lateinit var bgHandler: Handler
+ private lateinit var bgExecutor: Executor
+
private lateinit var wwm: WindowlessWindowManager
private lateinit var unfoldedDisplayInfo: DisplayInfo
private lateinit var overlayContainer: SurfaceControl
@@ -84,7 +85,12 @@ constructor(
private var currentRotation: Int = context.display!!.rotation
fun init() {
- deviceStateManager.registerCallback(executor, FoldListener())
+ // This method will be called only on devices where this animation is enabled,
+ // so normally this thread won't be created
+ bgHandler = threadFactory.buildHandlerOnNewThread(TAG)
+ bgExecutor = threadFactory.buildDelayableExecutorOnHandler(bgHandler)
+
+ deviceStateManager.registerCallback(bgExecutor, FoldListener())
unfoldTransitionProgressProvider.addCallback(transitionListener)
rotationChangeProvider.addCallback(rotationWatcher)
@@ -122,20 +128,23 @@ constructor(
* @param onOverlayReady callback when the overlay is drawn and visible on the screen
* @see [com.android.systemui.keyguard.KeyguardViewMediator]
*/
+ @BinderThread
fun onScreenTurningOn(onOverlayReady: Runnable) {
- Trace.beginSection("UnfoldLightRevealOverlayAnimation#onScreenTurningOn")
- try {
- // Add the view only if we are unfolding and this is the first screen on
- if (!isFolded && !isUnfoldHandled && contentResolver.areAnimationsEnabled()) {
- executeInBackground { addOverlay(onOverlayReady, reason = UNFOLD) }
- isUnfoldHandled = true
- } else {
- // No unfold transition, immediately report that overlay is ready
- executeInBackground { ensureOverlayRemoved() }
- onOverlayReady.run()
+ executeInBackground {
+ Trace.beginSection("$TAG#onScreenTurningOn")
+ try {
+ // Add the view only if we are unfolding and this is the first screen on
+ if (!isFolded && !isUnfoldHandled && contentResolver.areAnimationsEnabled()) {
+ addOverlay(onOverlayReady, reason = UNFOLD)
+ isUnfoldHandled = true
+ } else {
+ // No unfold transition, immediately report that overlay is ready
+ ensureOverlayRemoved()
+ onOverlayReady.run()
+ }
+ } finally {
+ Trace.endSection()
}
- } finally {
- Trace.endSection()
}
}
@@ -154,17 +163,18 @@ constructor(
LightRevealScrim(context, null).apply {
revealEffect = createLightRevealEffect()
isScrimOpaqueChangedListener = Consumer {}
- revealAmount = when (reason) {
- FOLD -> TRANSPARENT
- UNFOLD -> BLACK
- }
+ revealAmount =
+ when (reason) {
+ FOLD -> TRANSPARENT
+ UNFOLD -> BLACK
+ }
}
val params = getLayoutParams()
newRoot.setView(newView, params)
if (onOverlayReady != null) {
- Trace.beginAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0)
+ Trace.beginAsyncSection("$TAG#relayout", 0)
newRoot.relayout(params) { transaction ->
val vsyncId = Choreographer.getSfInstance().vsyncId
@@ -179,8 +189,8 @@ constructor(
transaction
.setFrameTimelineVsync(vsyncId + 1)
- .addTransactionCommittedListener(backgroundExecutor) {
- Trace.endAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0)
+ .addTransactionCommittedListener(bgExecutor) {
+ Trace.endAsyncSection("$TAG#relayout", 0)
onOverlayReady.run()
}
.apply()
@@ -233,7 +243,8 @@ constructor(
}
private fun getUnfoldedDisplayInfo(): DisplayInfo =
- displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+ displayManager
+ .getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
.asSequence()
.map { DisplayInfo().apply { it.getDisplayInfo(this) } }
.filter { it.type == Display.TYPE_INTERNAL }
@@ -261,10 +272,10 @@ constructor(
private inner class RotationWatcher : RotationChangeProvider.RotationListener {
override fun onRotationChanged(newRotation: Int) {
- traceSection("UnfoldLightRevealOverlayAnimation#onRotationChanged") {
- if (currentRotation != newRotation) {
- currentRotation = newRotation
- executeInBackground {
+ executeInBackground {
+ traceSection("$TAG#onRotationChanged") {
+ if (currentRotation != newRotation) {
+ currentRotation = newRotation
scrimView?.revealEffect = createLightRevealEffect()
root?.relayout(getLayoutParams())
}
@@ -274,7 +285,10 @@ constructor(
}
private fun executeInBackground(f: () -> Unit) {
- ensureInMainThread()
+ check(Looper.myLooper() != bgHandler.looper) {
+ "Trying to execute using background handler while already running" +
+ " in the background handler"
+ }
// The UiBackground executor is not used as it doesn't have a prepared looper.
bgHandler.post(f)
}
@@ -283,25 +297,25 @@ constructor(
check(Looper.myLooper() == bgHandler.looper) { "Not being executed in the background!" }
}
- private fun ensureInMainThread() {
- isMainThread()
- }
-
private inner class FoldListener :
FoldStateListener(
context,
Consumer { isFolded ->
if (isFolded) {
- executeInBackground { ensureOverlayRemoved() }
+ ensureOverlayRemoved()
isUnfoldHandled = false
}
this.isFolded = isFolded
}
)
- private enum class AddOverlayReason { FOLD, UNFOLD }
+ private enum class AddOverlayReason {
+ FOLD,
+ UNFOLD
+ }
private companion object {
+ const val TAG = "UnfoldLightRevealOverlayAnimation"
const val ROTATION_ANIMATION_OVERLAY_Z_INDEX = Integer.MAX_VALUE
// Put the unfold overlay below the rotation animation screenshot to hide the moment
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 7da2d47c1226..52b7fb63c1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -39,9 +39,9 @@ constructor(
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.user_switcher_fullscreen)
- window.decorView.getWindowInsetsController().apply {
- setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
- hide(Type.systemBars())
+ window.decorView.windowInsetsController?.let { controller ->
+ controller.systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ controller.hide(Type.systemBars())
}
val viewModel =
ViewModelProvider(this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt b/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt
index 6cd384f17803..ceebcb77fde2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt
@@ -25,8 +25,11 @@ import java.util.concurrent.atomic.AtomicReference
*/
class PendingTasksContainer {
- private var pendingTasksCount: AtomicInteger = AtomicInteger(0)
- private var completionCallback: AtomicReference<Runnable> = AtomicReference()
+ @Volatile
+ private var pendingTasksCount = AtomicInteger(0)
+
+ @Volatile
+ private var completionCallback = AtomicReference<Runnable>()
/**
* Registers a task that we should wait for
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 10595439200a..fa9bab28d5d8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -150,4 +150,10 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
getContext().getResources().getString(R.string.kg_prompt_reason_restart_password),
false);
}
+
+ @Test
+ public void testReset() {
+ mKeyguardAbsKeyInputViewController.reset();
+ verify(mKeyguardMessageAreaController).setMessage("", false);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
deleted file mode 100644
index fa8c8982bccb..000000000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ /dev/null
@@ -1,105 +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.keyguard
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class KeyguardListenQueueTest : SysuiTestCase() {
-
- @Test
- fun testQueueIsBounded() {
- val size = 5
- val queue = KeyguardListenQueue(sizePerModality = size)
-
- val fingerprints = List(100) { fingerprintModel(it) }
- fingerprints.forEach { queue.add(it) }
-
- assertThat(queue.models).containsExactlyElementsIn(fingerprints.takeLast(size))
-
- val faces = List(100) { faceModel(it) }
- faces.forEach { queue.add(it) }
-
- assertThat(queue.models).containsExactlyElementsIn(
- faces.takeLast(size) + fingerprints.takeLast(5)
- )
-
- repeat(100) {
- queue.add(faceModel(-1))
- queue.add(fingerprintModel(-1))
- }
- assertThat(queue.models).hasSize(2 * size)
- assertThat(queue.models.count { it.userId == -1 }).isEqualTo(2 * size)
- }
-}
-
-private fun fingerprintModel(user: Int) = KeyguardFingerprintListenModel(
- timeMillis = System.currentTimeMillis(),
- userId = user,
- listening = false,
- biometricEnabledForUser = false,
- bouncerIsOrWillShow = false,
- canSkipBouncer = false,
- credentialAttempted = false,
- deviceInteractive = false,
- dreaming = false,
- fingerprintDisabled = false,
- fingerprintLockedOut = false,
- goingToSleep = false,
- keyguardGoingAway = false,
- keyguardIsVisible = false,
- keyguardOccluded = false,
- occludingAppRequestingFp = false,
- primaryUser = false,
- shouldListenSfpsState = false,
- shouldListenForFingerprintAssistant = false,
- strongerAuthRequired = false,
- switchingUser = false,
- udfps = false,
- userDoesNotHaveTrust = false
-)
-
-private fun faceModel(user: Int) = KeyguardFaceListenModel(
- timeMillis = System.currentTimeMillis(),
- userId = user,
- listening = false,
- authInterruptActive = false,
- biometricSettingEnabledForUser = false,
- bouncerFullyShown = false,
- faceAndFpNotAuthenticated = false,
- faceAuthAllowed = true,
- faceDisabled = false,
- faceLockedOut = false,
- goingToSleep = false,
- keyguardAwake = false,
- keyguardGoingAway = false,
- listeningForFaceAssistant = false,
- occludingAppRequestingFaceAuth = false,
- primaryUser = false,
- secureCameraLaunched = false,
- supportsDetect = true,
- switchingUser = false,
- udfpsBouncerShowing = false,
- udfpsFingerDown = false,
- userNotTrustedOrDetectionIsNeeded = false
-)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 0f4cf4119731..539dc5536d1a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -823,7 +823,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
lockscreenBypassIsAllowed();
- mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
new ArrayList<>());
keyguardIsVisible();
@@ -834,7 +834,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void testIgnoresAuth_whenTrustAgentOnKeyguard_withoutBypass() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
- mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
keyguardIsVisible();
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
@@ -1049,8 +1049,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testGetUserCanSkipBouncer_whenTrust() {
int user = KeyguardUpdateMonitor.getCurrentUser();
- mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */,
- new ArrayList<>());
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
+ user, 0 /* flags */, new ArrayList<>());
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@@ -1313,7 +1313,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
// WHEN trust is enabled (ie: via smartlock)
- mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
// THEN we shouldn't listen for udfps
@@ -1417,13 +1417,14 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShowTrustGrantedMessage_onTrustGranted() {
// WHEN trust is enabled (ie: via some trust agent) with a trustGranted string
- mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
Arrays.asList("Unlocked by wearable"));
// THEN the showTrustGrantedMessage should be called with the first message
verify(mTestCallback).onTrustGrantedForCurrentUser(
- anyBoolean(),
+ anyBoolean() /* dismissKeyguard */,
+ eq(true) /* newlyUnlocked */,
eq(new TrustGrantFlags(0)),
eq("Unlocked by wearable"));
}
@@ -1869,6 +1870,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN onTrustChanged with TRUST_DISMISS_KEYGUARD flag
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
+ true /* newlyUnlocked */,
getCurrentUser() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
null /* trustGrantedMessages */);
@@ -1876,6 +1878,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// THEN onTrustGrantedForCurrentUser callback called
verify(callback).onTrustGrantedForCurrentUser(
eq(true) /* dismissKeyguard */,
+ eq(true) /* newlyUnlocked */,
eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD)),
eq(null) /* message */
);
@@ -1892,6 +1895,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN onTrustChanged with TRUST_DISMISS_KEYGUARD flag
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
+ true /* newlyUnlocked */,
getCurrentUser() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
null /* trustGrantedMessages */);
@@ -1899,6 +1903,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// THEN onTrustGrantedForCurrentUser callback called
verify(callback).onTrustGrantedForCurrentUser(
eq(false) /* dismissKeyguard */,
+ eq(true) /* newlyUnlocked */,
eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD)),
eq(null) /* message */
);
@@ -1916,6 +1921,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN onTrustChanged for a different user
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
+ true /* newlyUnlocked */,
546 /* userId, not the current userId */,
0 /* flags */,
null /* trustGrantedMessages */);
@@ -1923,6 +1929,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// THEN onTrustGrantedForCurrentUser callback called
verify(callback, never()).onTrustGrantedForCurrentUser(
anyBoolean() /* dismissKeyguard */,
+ eq(true) /* newlyUnlocked */,
anyObject() /* flags */,
anyString() /* message */
);
@@ -1940,6 +1947,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// flags (temporary & rewable is active unlock)
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
+ true /* newlyUnlocked */,
getCurrentUser() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
| TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */,
@@ -1948,6 +1956,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// THEN onTrustGrantedForCurrentUser callback called
verify(callback).onTrustGrantedForCurrentUser(
eq(true) /* dismissKeyguard */,
+ eq(true) /* newlyUnlocked */,
eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
| TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)),
eq(null) /* message */
@@ -1967,6 +1976,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN onTrustChanged with INITIATED_BY_USER flag
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
+ true /* newlyUnlocked */,
getCurrentUser() /* userId, not the current userId */,
TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER /* flags */,
null /* trustGrantedMessages */);
@@ -1974,6 +1984,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// THEN onTrustGrantedForCurrentUser callback called
verify(callback, never()).onTrustGrantedForCurrentUser(
eq(true) /* dismissKeyguard */,
+ eq(true) /* newlyUnlocked */,
eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER)),
anyString() /* message */
);
@@ -1991,6 +2002,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN onTrustChanged with INITIATED_BY_USER flag
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
+ true /* newlyUnlocked */,
getCurrentUser() /* userId, not the current userId */,
TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER
| TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */,
@@ -1999,6 +2011,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// THEN onTrustGrantedForCurrentUser callback called
verify(callback, never()).onTrustGrantedForCurrentUser(
eq(true) /* dismissKeyguard */,
+ eq(true) /* newlyUnlocked */,
eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER
| TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)),
anyString() /* message */
@@ -2309,6 +2322,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private void currentUserDoesNotHaveTrust() {
mKeyguardUpdateMonitor.onTrustChanged(
false,
+ false,
KeyguardUpdateMonitor.getCurrentUser(),
-1,
new ArrayList<>()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
index 5734c3de70e0..34e78eb8c2eb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
@@ -52,8 +52,6 @@ class ScreenOnCoordinatorTest : SysuiTestCase() {
private lateinit var foldAodAnimationController: FoldAodAnimationController
@Mock
private lateinit var unfoldAnimation: UnfoldLightRevealOverlayAnimation
- @Mock
- private lateinit var screenLifecycle: ScreenLifecycle
@Captor
private lateinit var readyCaptor: ArgumentCaptor<Runnable>
@@ -69,13 +67,8 @@ class ScreenOnCoordinatorTest : SysuiTestCase() {
.thenReturn(foldAodAnimationController)
screenOnCoordinator = ScreenOnCoordinator(
- screenLifecycle,
Optional.of(unfoldComponent),
- FakeExecution()
)
-
- // Make sure screen events are registered to observe
- verify(screenLifecycle).addObserver(screenOnCoordinator)
}
@Test
@@ -93,9 +86,7 @@ class ScreenOnCoordinatorTest : SysuiTestCase() {
fun testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback() {
// Recreate with empty unfoldComponent
screenOnCoordinator = ScreenOnCoordinator(
- screenLifecycle,
Optional.empty(),
- FakeExecution()
)
screenOnCoordinator.onScreenTurningOn(runnable)
@@ -105,11 +96,11 @@ class ScreenOnCoordinatorTest : SysuiTestCase() {
private fun onUnfoldOverlayReady() {
verify(unfoldAnimation).onScreenTurningOn(capture(readyCaptor))
- readyCaptor.getValue().run()
+ readyCaptor.value.run()
}
private fun onFoldAodReady() {
verify(foldAodAnimationController).onScreenTurningOn(capture(readyCaptor))
- readyCaptor.getValue().run()
+ readyCaptor.value.run()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 30dc9c911876..67b293f44cf4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -20,8 +20,6 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
-
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -35,12 +33,11 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.same;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -90,9 +87,9 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
+import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -108,7 +105,6 @@ import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -117,8 +113,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Random;
-import javax.inject.Provider;
-
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -162,7 +156,7 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private LockPatternUtils mLockPatternUtils;
@Mock
- private StatusBarStateController mStatusBarStateController;
+ private LogContextInteractor mLogContextInteractor;
@Mock
private UdfpsLogger mUdfpsLogger;
@Mock
@@ -178,10 +172,6 @@ public class AuthControllerTest extends SysuiTestCase {
private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor;
@Captor
private ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor;
- @Captor
- private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
- @Captor
- private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefullnessObserverCaptor;
private TestableContext mContextSpy;
private Execution mExecution;
@@ -253,10 +243,7 @@ public class AuthControllerTest extends SysuiTestCase {
when(mFaceManager.getSensorPropertiesInternal()).thenReturn(faceProps);
when(mVibratorHelper.hasVibrator()).thenReturn(true);
- mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
- mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
- () -> mUdfpsController, () -> mSideFpsController, mStatusBarStateController,
- mVibratorHelper);
+ mAuthController = new TestableAuthController(mContextSpy);
mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -264,11 +251,6 @@ public class AuthControllerTest extends SysuiTestCase {
verify(mFaceManager).addAuthenticatorsRegisteredCallback(
mFaceAuthenticatorsRegisteredCaptor.capture());
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
- verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
- verify(mWakefulnessLifecycle).addObserver(mWakefullnessObserverCaptor.capture());
-
mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps);
mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps);
@@ -286,10 +268,7 @@ public class AuthControllerTest extends SysuiTestCase {
reset(mFaceManager);
// This test requires an uninitialized AuthController.
- AuthController authController = new TestableAuthController(mContextSpy, mExecution,
- mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
- mFaceManager, () -> mUdfpsController, () -> mSideFpsController,
- mStatusBarStateController, mVibratorHelper);
+ AuthController authController = new TestableAuthController(mContextSpy);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -316,10 +295,7 @@ public class AuthControllerTest extends SysuiTestCase {
reset(mFaceManager);
// This test requires an uninitialized AuthController.
- AuthController authController = new TestableAuthController(mContextSpy, mExecution,
- mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
- mFaceManager, () -> mUdfpsController, () -> mSideFpsController,
- mStatusBarStateController, mVibratorHelper);
+ AuthController authController = new TestableAuthController(mContextSpy);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -809,37 +785,9 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
- public void testForwardsDozeEvents() throws RemoteException {
- when(mStatusBarStateController.isDozing()).thenReturn(true);
- when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
- mAuthController.setBiometricContextListener(mContextListener);
-
- mStatusBarStateListenerCaptor.getValue().onDozingChanged(true);
- mStatusBarStateListenerCaptor.getValue().onDozingChanged(false);
-
- InOrder order = inOrder(mContextListener);
- order.verify(mContextListener, times(2)).onDozeChanged(eq(true), eq(true));
- order.verify(mContextListener).onDozeChanged(eq(false), eq(true));
- order.verifyNoMoreInteractions();
- }
-
- @Test
- public void testForwardsWakeEvents() throws RemoteException {
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
+ public void testSubscribesToLogContext() {
mAuthController.setBiometricContextListener(mContextListener);
-
- mWakefullnessObserverCaptor.getValue().onStartedGoingToSleep();
- mWakefullnessObserverCaptor.getValue().onFinishedGoingToSleep();
- mWakefullnessObserverCaptor.getValue().onStartedWakingUp();
- mWakefullnessObserverCaptor.getValue().onFinishedWakingUp();
- mWakefullnessObserverCaptor.getValue().onPostFinishedWakingUp();
-
- InOrder order = inOrder(mContextListener);
- order.verify(mContextListener).onDozeChanged(eq(false), eq(true));
- order.verify(mContextListener).onDozeChanged(eq(false), eq(false));
- order.verify(mContextListener).onDozeChanged(eq(false), eq(true));
- order.verifyNoMoreInteractions();
+ verify(mLogContextInteractor).addBiometricContextListener(same(mContextListener));
}
@Test
@@ -1001,23 +949,13 @@ public class AuthControllerTest extends SysuiTestCase {
private int mBuildCount = 0;
private PromptInfo mLastBiometricPromptInfo;
- TestableAuthController(Context context,
- Execution execution,
- CommandQueue commandQueue,
- ActivityTaskManager activityTaskManager,
- WindowManager windowManager,
- FingerprintManager fingerprintManager,
- FaceManager faceManager,
- Provider<UdfpsController> udfpsControllerFactory,
- Provider<SideFpsController> sidefpsControllerFactory,
- StatusBarStateController statusBarStateController,
- VibratorHelper vibratorHelper) {
- super(context, execution, commandQueue, activityTaskManager, windowManager,
- fingerprintManager, faceManager, udfpsControllerFactory,
- sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle,
- mUserManager, mLockPatternUtils, mUdfpsLogger, statusBarStateController,
+ TestableAuthController(Context context) {
+ super(context, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager,
+ mFingerprintManager, mFaceManager, () -> mUdfpsController,
+ () -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle,
+ mUserManager, mLockPatternUtils, mUdfpsLogger, mLogContextInteractor,
() -> mBiometricPromptCredentialInteractor, () -> mCredentialViewModel,
- mInteractionJankMonitor, mHandler, mBackgroundExecutor, vibratorHelper);
+ mInteractionJankMonitor, mHandler, mBackgroundExecutor, mVibratorHelper);
}
@Override
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 0c34e54d2ec4..83a6db032858 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -68,6 +68,7 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
@@ -83,6 +84,7 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -204,6 +206,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
@Mock
+ private SessionTracker mSessionTracker;
+ @Mock
private AlternateBouncerInteractor mAlternateBouncerInteractor;
// Capture listeners so that they can be used to send events
@@ -240,6 +244,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
.thenReturn(mFpmOtherView);
when(mEnrollView.getContext()).thenReturn(mContext);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+ when(mSessionTracker.getSessionId(anyInt())).thenReturn(
+ (new InstanceIdSequence(1 << 20)).newInstanceId());
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
@@ -295,7 +301,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mDisplayManager, mHandler, mConfigurationController, mSystemClock,
mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker,
mActivityLaunchAnimator, alternateTouchProvider, mBiometricExecutor,
- mPrimaryBouncerInteractor, mSinglePointerTouchProcessor,
+ mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, mSessionTracker,
mAlternateBouncerInteractor);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
new file mode 100644
index 000000000000..3eb82a7d69bb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
@@ -0,0 +1,198 @@
+package com.android.systemui.biometrics.domain.interactor
+
+import android.hardware.biometrics.IBiometricContextListener
+import android.hardware.biometrics.IBiometricContextListener.FoldState
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class LogContextInteractorImplTest : SysuiTestCase() {
+
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private val testScope = TestScope()
+
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock private lateinit var foldProvider: FoldStateProvider
+
+ private lateinit var interactor: LogContextInteractorImpl
+
+ @Before
+ fun setup() {
+ interactor =
+ LogContextInteractorImpl(
+ testScope.backgroundScope,
+ statusBarStateController,
+ wakefulnessLifecycle,
+ foldProvider
+ )
+ }
+
+ @Test
+ fun isDozingChanges() =
+ testScope.runTest {
+ whenever(statusBarStateController.isDozing).thenReturn(true)
+
+ val isDozing = collectLastValue(interactor.isDozing)
+ runCurrent()
+ val listener = statusBarStateController.captureListener()
+
+ assertThat(isDozing()).isTrue()
+
+ listener.onDozingChanged(true)
+ listener.onDozingChanged(true)
+ listener.onDozingChanged(false)
+
+ assertThat(isDozing()).isFalse()
+ }
+
+ @Test
+ fun isAwakeChanges() =
+ testScope.runTest {
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
+
+ val isAwake = collectLastValue(interactor.isAwake)
+ runCurrent()
+ val listener = wakefulnessLifecycle.captureObserver()
+
+ assertThat(isAwake()).isTrue()
+
+ listener.onStartedGoingToSleep()
+ listener.onFinishedGoingToSleep()
+ listener.onStartedWakingUp()
+
+ assertThat(isAwake()).isFalse()
+
+ listener.onFinishedWakingUp()
+ listener.onPostFinishedWakingUp()
+
+ assertThat(isAwake()).isTrue()
+ }
+
+ @Test
+ fun foldStateChanges() =
+ testScope.runTest {
+ val foldState = collectLastValue(interactor.foldState)
+ runCurrent()
+ val listener = foldProvider.captureListener()
+
+ listener.onFoldUpdate(FOLD_UPDATE_START_OPENING)
+ assertThat(foldState()).isEqualTo(FoldState.UNKNOWN)
+
+ listener.onFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+ assertThat(foldState()).isEqualTo(FoldState.HALF_OPENED)
+
+ listener.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+ assertThat(foldState()).isEqualTo(FoldState.FULLY_OPENED)
+
+ listener.onFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ assertThat(foldState()).isEqualTo(FoldState.FULLY_OPENED)
+
+ listener.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+ assertThat(foldState()).isEqualTo(FoldState.FULLY_CLOSED)
+ }
+
+ @Test
+ fun contextSubscriberChanges() =
+ testScope.runTest {
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
+ runCurrent()
+
+ val foldListener = foldProvider.captureListener()
+ foldListener.onFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+ var dozing: Boolean? = null
+ var awake: Boolean? = null
+ var folded: Int? = null
+ val job =
+ interactor.addBiometricContextListener(
+ object : IBiometricContextListener.Stub() {
+ override fun onDozeChanged(isDozing: Boolean, isAwake: Boolean) {
+ dozing = isDozing
+ awake = isAwake
+ }
+
+ override fun onFoldChanged(foldState: Int) {
+ folded = foldState
+ }
+ }
+ )
+ runCurrent()
+
+ val statusBarStateListener = statusBarStateController.captureListener()
+ val wakefullnessObserver = wakefulnessLifecycle.captureObserver()
+
+ assertThat(dozing).isFalse()
+ assertThat(awake).isTrue()
+ assertThat(folded).isEqualTo(FoldState.FULLY_CLOSED)
+
+ statusBarStateListener.onDozingChanged(true)
+ wakefullnessObserver.onStartedGoingToSleep()
+ foldListener.onFoldUpdate(FOLD_UPDATE_START_OPENING)
+ foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+ wakefullnessObserver.onFinishedGoingToSleep()
+ runCurrent()
+
+ assertThat(dozing).isTrue()
+ assertThat(awake).isFalse()
+ assertThat(folded).isEqualTo(FoldState.HALF_OPENED)
+
+ job.cancel()
+
+ // stale updates should be ignored
+ statusBarStateListener.onDozingChanged(false)
+ wakefullnessObserver.onFinishedWakingUp()
+ foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+ runCurrent()
+
+ assertThat(dozing).isTrue()
+ assertThat(awake).isFalse()
+ assertThat(folded).isEqualTo(FoldState.HALF_OPENED)
+ }
+}
+
+private fun StatusBarStateController.captureListener() =
+ withArgCaptor<StatusBarStateController.StateListener> {
+ verify(this@captureListener).addCallback(capture())
+ }
+
+private fun WakefulnessLifecycle.captureObserver() =
+ withArgCaptor<WakefulnessLifecycle.Observer> {
+ verify(this@captureObserver).addObserver(capture())
+ }
+
+private fun FoldStateProvider.captureListener() =
+ withArgCaptor<FoldStateProvider.FoldUpdatesListener> {
+ verify(this@captureListener).addCallback(capture())
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
index 09c8e6ac1268..cf9c91a1adb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
@@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
@@ -84,6 +85,7 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
@Mock private lateinit var previewRenderer: KeyguardPreviewRenderer
@Mock private lateinit var backgroundHandler: Handler
@Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
+ @Mock private lateinit var launchAnimator: DialogLaunchAnimator
private lateinit var underTest: KeyguardQuickAffordanceProvider
@@ -167,8 +169,10 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
+ set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
},
repository = { quickAffordanceRepository },
+ launchAnimator = launchAnimator,
)
underTest.previewManager =
KeyguardRemotePreviewManager(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
index f46d58d679b5..70a0415d2e35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
@@ -58,15 +58,15 @@ public class ScreenLifecycleTest extends SysuiTestCase {
@Test
public void screenTurningOn() throws Exception {
Runnable onDrawn = () -> {};
- mScreen.dispatchScreenTurningOn(onDrawn);
+ mScreen.dispatchScreenTurningOn();
assertEquals(ScreenLifecycle.SCREEN_TURNING_ON, mScreen.getScreenState());
- verify(mScreenObserverMock).onScreenTurningOn(onDrawn);
+ verify(mScreenObserverMock).onScreenTurningOn();
}
@Test
public void screenTurnedOn() throws Exception {
- mScreen.dispatchScreenTurningOn(null);
+ mScreen.dispatchScreenTurningOn();
mScreen.dispatchScreenTurnedOn();
assertEquals(ScreenLifecycle.SCREEN_ON, mScreen.getScreenState());
@@ -75,7 +75,7 @@ public class ScreenLifecycleTest extends SysuiTestCase {
@Test
public void screenTurningOff() throws Exception {
- mScreen.dispatchScreenTurningOn(null);
+ mScreen.dispatchScreenTurningOn();
mScreen.dispatchScreenTurnedOn();
mScreen.dispatchScreenTurningOff();
@@ -85,7 +85,7 @@ public class ScreenLifecycleTest extends SysuiTestCase {
@Test
public void screenTurnedOff() throws Exception {
- mScreen.dispatchScreenTurningOn(null);
+ mScreen.dispatchScreenTurningOn();
mScreen.dispatchScreenTurnedOn();
mScreen.dispatchScreenTurningOff();
mScreen.dispatchScreenTurnedOff();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
new file mode 100644
index 000000000000..7c10108d5b45
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -0,0 +1,251 @@
+/*
+ * 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.quickaffordance
+
+import android.net.Uri
+import android.provider.Settings
+import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+import android.provider.Settings.Global.ZEN_MODE_OFF
+import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
+import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
+import androidx.test.filters.SmallTest
+import com.android.settingslib.notification.EnableZenModeDialog
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.FakeSettings
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+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.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
+
+ @Mock private lateinit var zenModeController: ZenModeController
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var conditionUri: Uri
+ @Mock private lateinit var enableZenModeDialog: EnableZenModeDialog
+ @Captor private lateinit var spyZenMode: ArgumentCaptor<Int>
+ @Captor private lateinit var spyConditionId: ArgumentCaptor<Uri?>
+ private lateinit var settings: FakeSettings
+
+ private lateinit var underTest: DoNotDisturbQuickAffordanceConfig
+ private lateinit var testDispatcher: TestDispatcher
+ private lateinit var testScope: TestScope
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
+
+ settings = FakeSettings()
+
+ underTest = DoNotDisturbQuickAffordanceConfig(
+ context,
+ zenModeController,
+ settings,
+ userTracker,
+ testDispatcher,
+ conditionUri,
+ enableZenModeDialog,
+ )
+ }
+
+ @Test
+ fun `dnd not available - picker state hidden`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(false)
+
+ //when
+ val result = underTest.getPickerScreenState()
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice, result)
+ }
+
+ @Test
+ fun `dnd available - picker state visible`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+
+ //when
+ val result = underTest.getPickerScreenState()
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default, result)
+ }
+
+ @Test
+ fun `onTriggered - dnd mode is not ZEN_MODE_OFF - set to ZEN_MODE_OFF`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(-1)
+ settings.putInt(Settings.Secure.ZEN_DURATION, -2)
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ //when
+ val result = underTest.onTriggered(null)
+ verify(zenModeController).setZen(spyZenMode.capture(), spyConditionId.capture(), eq(DoNotDisturbQuickAffordanceConfig.TAG))
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(ZEN_MODE_OFF, spyZenMode.value)
+ assertNull(spyConditionId.value)
+ }
+
+ @Test
+ fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is FOREVER - set zen with no condition`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ settings.putInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_FOREVER)
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ //when
+ val result = underTest.onTriggered(null)
+ verify(zenModeController).setZen(spyZenMode.capture(), spyConditionId.capture(), eq(DoNotDisturbQuickAffordanceConfig.TAG))
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
+ assertNull(spyConditionId.value)
+ }
+
+ @Test
+ fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is not FOREVER or PROMPT - set zen with condition`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ settings.putInt(Settings.Secure.ZEN_DURATION, -900)
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ //when
+ val result = underTest.onTriggered(null)
+ verify(zenModeController).setZen(spyZenMode.capture(), spyConditionId.capture(), eq(DoNotDisturbQuickAffordanceConfig.TAG))
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
+ assertEquals(conditionUri, spyConditionId.value)
+ }
+
+ @Test
+ fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is PROMPT - show dialog`() = testScope.runTest {
+ //given
+ val expandable: Expandable = mock()
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ settings.putInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_PROMPT)
+ whenever(enableZenModeDialog.createDialog()).thenReturn(mock())
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ //when
+ val result = underTest.onTriggered(expandable)
+
+ //then
+ assertTrue(result is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog)
+ assertEquals(expandable, (result as KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog).expandable)
+ }
+
+ @Test
+ fun `lockScreenState - dndAvailable starts as true - changes to false - State moves to Hidden`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ val callbackCaptor: ArgumentCaptor<ZenModeController.Callback> = argumentCaptor()
+ val valueSnapshot = collectLastValue(underTest.lockScreenState)
+ val secondLastValue = valueSnapshot()
+ verify(zenModeController).addCallback(callbackCaptor.capture())
+
+ //when
+ callbackCaptor.value.onZenAvailableChanged(false)
+ val lastValue = valueSnapshot()
+
+ //then
+ assertTrue(secondLastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
+
+ @Test
+ fun `lockScreenState - dndMode starts as ZEN_MODE_OFF - changes to not OFF - State moves to Visible`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ val valueSnapshot = collectLastValue(underTest.lockScreenState)
+ val secondLastValue = valueSnapshot()
+ val callbackCaptor: ArgumentCaptor<ZenModeController.Callback> = argumentCaptor()
+ verify(zenModeController).addCallback(callbackCaptor.capture())
+
+ //when
+ callbackCaptor.value.onZenChanged(ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ val lastValue = valueSnapshot()
+
+ //then
+ assertEquals(
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_dnd_icon_off,
+ ContentDescription.Resource(R.string.dnd_is_off)
+ ),
+ ActivationState.Inactive
+ ),
+ secondLastValue,
+ )
+ assertEquals(
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_dnd_icon_on,
+ ContentDescription.Resource(R.string.dnd_is_on)
+ ),
+ ActivationState.Active
+ ),
+ lastValue,
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index ce9c1da422f5..5d2f0eb01de1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -16,13 +16,10 @@
package com.android.systemui.keyguard.data.repository
-import android.animation.AnimationHandler.AnimationFrameCallbackProvider
import android.animation.ValueAnimator
import android.util.Log
import android.util.Log.TerribleFailure
import android.util.Log.TerribleFailureHandler
-import android.view.Choreographer.FrameCallback
-import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
@@ -32,22 +29,17 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.util.KeyguardTransitionRunner
import com.google.common.truth.Truth.assertThat
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.UUID
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.yield
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.After
-import org.junit.Assert.fail
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,12 +52,14 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
private lateinit var underTest: KeyguardTransitionRepository
private lateinit var oldWtfHandler: TerribleFailureHandler
private lateinit var wtfHandler: WtfHandler
+ private lateinit var runner: KeyguardTransitionRunner
@Before
fun setUp() {
underTest = KeyguardTransitionRepositoryImpl()
wtfHandler = WtfHandler()
oldWtfHandler = Log.setWtfHandler(wtfHandler)
+ runner = KeyguardTransitionRunner(underTest)
}
@After
@@ -75,56 +69,37 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
@Test
fun `startTransition runs animator to completion`() =
- runBlocking(IMMEDIATE) {
- val (animator, provider) = setupAnimator(this)
-
+ TestScope().runTest {
val steps = mutableListOf<TransitionStep>()
val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
- underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator))
-
- val startTime = System.currentTimeMillis()
- while (animator.isRunning()) {
- yield()
- if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) {
- fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION")
- }
- }
+ runner.startTransition(
+ this,
+ TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()),
+ maxFrames = 100
+ )
assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN)
-
job.cancel()
- provider.stop()
}
@Test
- @FlakyTest(bugId = 260213291)
- fun `starting second transition will cancel the first transition`() {
- runBlocking(IMMEDIATE) {
- val (animator, provider) = setupAnimator(this)
-
+ fun `starting second transition will cancel the first transition`() =
+ TestScope().runTest {
val steps = mutableListOf<TransitionStep>()
val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
-
- underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator))
- // 3 yields(), alternating with the animator, results in a value 0.1, which can be
- // canceled and tested against
- yield()
- yield()
- yield()
+ runner.startTransition(
+ this,
+ TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()),
+ maxFrames = 3,
+ )
// Now start 2nd transition, which will interrupt the first
val job2 = underTest.transition(LOCKSCREEN, AOD).onEach { steps.add(it) }.launchIn(this)
- val (animator2, provider2) = setupAnimator(this)
- underTest.startTransition(TransitionInfo(OWNER_NAME, LOCKSCREEN, AOD, animator2))
-
- val startTime = System.currentTimeMillis()
- while (animator2.isRunning()) {
- yield()
- if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) {
- fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION")
- }
- }
+ runner.startTransition(
+ this,
+ TransitionInfo(OWNER_NAME, LOCKSCREEN, AOD, getAnimator()),
+ )
val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1))
assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN)
@@ -134,31 +109,25 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
job.cancel()
job2.cancel()
- provider.stop()
- provider2.stop()
}
- }
@Test
fun `Null animator enables manual control with updateTransition`() =
- runBlocking(IMMEDIATE) {
+ TestScope().runTest {
val steps = mutableListOf<TransitionStep>()
val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
val uuid =
underTest.startTransition(
- TransitionInfo(
- ownerName = OWNER_NAME,
- from = AOD,
- to = LOCKSCREEN,
- animator = null,
- )
+ TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator = null)
)
+ runCurrent()
checkNotNull(uuid).let {
underTest.updateTransition(it, 0.5f, TransitionState.RUNNING)
underTest.updateTransition(it, 1f, TransitionState.FINISHED)
}
+ runCurrent()
assertThat(steps.size).isEqualTo(3)
assertThat(steps[0])
@@ -256,57 +225,11 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
assertThat(wtfHandler.failed).isFalse()
}
- private fun setupAnimator(
- scope: CoroutineScope
- ): Pair<ValueAnimator, TestFrameCallbackProvider> {
- val animator =
- ValueAnimator().apply {
- setInterpolator(Interpolators.LINEAR)
- setDuration(ANIMATION_DURATION)
- }
-
- val provider = TestFrameCallbackProvider(animator, scope)
- provider.start()
-
- return Pair(animator, provider)
- }
-
- /** Gives direct control over ValueAnimator. See [AnimationHandler] */
- private class TestFrameCallbackProvider(
- private val animator: ValueAnimator,
- private val scope: CoroutineScope,
- ) : AnimationFrameCallbackProvider {
-
- private var frameCount = 1L
- private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null))
- private var job: Job? = null
-
- fun start() {
- animator.getAnimationHandler().setProvider(this)
-
- job =
- scope.launch {
- frames.collect {
- // Delay is required for AnimationHandler to properly register a callback
- yield()
- val (frameNumber, callback) = it
- callback?.doFrame(frameNumber)
- }
- }
- }
-
- fun stop() {
- job?.cancel()
- animator.getAnimationHandler().setProvider(null)
- }
-
- override fun postFrameCallback(cb: FrameCallback) {
- frames.value = Pair(frameCount++, cb)
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(10)
}
- override fun postCommitCallback(runnable: Runnable) {}
- override fun getFrameTime() = frameCount
- override fun getFrameDelay() = 1L
- override fun setFrameDelay(delay: Long) {}
}
private class WtfHandler : TerribleFailureHandler {
@@ -317,9 +240,6 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
}
companion object {
- private const val MAX_TEST_DURATION = 100L
- private const val ANIMATION_DURATION = 10L
- private const val OWNER_NAME = "Test"
- private val IMMEDIATE = Dispatchers.Main.immediate
+ private const val OWNER_NAME = "KeyguardTransitionRunner"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 1c1f0399bb06..14b7c311d9af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -23,6 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -214,6 +215,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
@Mock private lateinit var expandable: Expandable
+ @Mock private lateinit var launchAnimator: DialogLaunchAnimator
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -308,6 +310,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
},
repository = { quickAffordanceRepository },
+ launchAnimator = launchAnimator,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 58a85306eb69..972919a9d27a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -22,6 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
@@ -73,6 +74,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var launchAnimator: DialogLaunchAnimator
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -171,6 +173,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
activityStarter = activityStarter,
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
+ launchAnimator = launchAnimator,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
new file mode 100644
index 000000000000..a6cf84053861
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -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 com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.keyguard.util.KeyguardTransitionRunner
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+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
+
+/**
+ * Class for testing user journeys through the interactors. They will all be activated during setup,
+ * to ensure the expected transitions are still triggered.
+ */
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardTransitionScenariosTest : SysuiTestCase() {
+ private lateinit var testScope: TestScope
+
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var shadeRepository: ShadeRepository
+
+ // Used to issue real transition steps for test input
+ private lateinit var runner: KeyguardTransitionRunner
+ private lateinit var transitionRepository: KeyguardTransitionRepository
+
+ // Used to verify transition requests for test output
+ @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository
+
+ private lateinit var lockscreenBouncerTransitionInteractor:
+ LockscreenBouncerTransitionInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testScope = TestScope()
+
+ keyguardRepository = FakeKeyguardRepository()
+ shadeRepository = FakeShadeRepository()
+
+ /* Used to issue full transition steps, to better simulate a real device */
+ transitionRepository = KeyguardTransitionRepositoryImpl()
+ runner = KeyguardTransitionRunner(transitionRepository)
+
+ lockscreenBouncerTransitionInteractor =
+ LockscreenBouncerTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = KeyguardInteractor(keyguardRepository),
+ shadeRepository = shadeRepository,
+ keyguardTransitionRepository = mockTransitionRepository,
+ keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ )
+ lockscreenBouncerTransitionInteractor.start()
+ }
+
+ @Test
+ fun `LOCKSCREEN to BOUNCER via bouncer showing call`() =
+ testScope.runTest {
+ // GIVEN a device that has at least woken up
+ keyguardRepository.setWakefulnessModel(startingToWake())
+ runCurrent()
+
+ // GIVEN a transition has run to LOCKSCREEN
+ runner.startTransition(
+ testScope,
+ TransitionInfo(
+ ownerName = "",
+ from = KeyguardState.OFF,
+ to = KeyguardState.LOCKSCREEN,
+ animator =
+ ValueAnimator().apply {
+ duration = 10
+ interpolator = Interpolators.LINEAR
+ },
+ )
+ )
+ runCurrent()
+
+ // WHEN the bouncer is set to show
+ keyguardRepository.setBouncerShowing(true)
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(mockTransitionRepository).startTransition(capture())
+ }
+ // THEN a transition to BOUNCER should occur
+ assertThat(info.ownerName).isEqualTo("LockscreenBouncerTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info.to).isEqualTo(KeyguardState.BOUNCER)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ private fun startingToWake() =
+ WakefulnessModel(
+ WakefulnessState.STARTING_TO_WAKE,
+ true,
+ WakeSleepReason.OTHER,
+ WakeSleepReason.OTHER
+ )
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 0abff88b5faf..a2c2f711b1d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -21,6 +21,7 @@ import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
@@ -82,6 +83,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var launchAnimator: DialogLaunchAnimator
private lateinit var underTest: KeyguardBottomAreaViewModel
@@ -191,6 +193,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
},
repository = { quickAffordanceRepository },
+ launchAnimator = launchAnimator,
),
bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
burnInHelperWrapper = burnInHelperWrapper,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
new file mode 100644
index 000000000000..c88f84a028ed
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
@@ -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.systemui.keyguard.util
+
+import android.animation.AnimationHandler.AnimationFrameCallbackProvider
+import android.animation.ValueAnimator
+import android.view.Choreographer.FrameCallback
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.junit.Assert.fail
+
+/**
+ * Gives direct control over ValueAnimator, in order to make transition tests deterministic. See
+ * [AnimationHandler]. Animators are required to be run on the main thread, so dispatch accordingly.
+ */
+class KeyguardTransitionRunner(
+ val repository: KeyguardTransitionRepository,
+) : AnimationFrameCallbackProvider {
+
+ private var frameCount = 1L
+ private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null))
+ private var job: Job? = null
+ private var isTerminated = false
+
+ /**
+ * For transitions being directed by an animator. Will control the number of frames being
+ * generated so the values are deterministic.
+ */
+ suspend fun startTransition(scope: CoroutineScope, info: TransitionInfo, maxFrames: Int = 100) {
+ // AnimationHandler uses ThreadLocal storage, and ValueAnimators MUST start from main
+ // thread
+ withContext(Dispatchers.Main) {
+ info.animator!!.getAnimationHandler().setProvider(this@KeyguardTransitionRunner)
+ }
+
+ job =
+ scope.launch {
+ frames.collect {
+ val (frameNumber, callback) = it
+
+ isTerminated = frameNumber >= maxFrames
+ if (!isTerminated) {
+ withContext(Dispatchers.Main) { callback?.doFrame(frameNumber) }
+ }
+ }
+ }
+ withContext(Dispatchers.Main) { repository.startTransition(info) }
+
+ waitUntilComplete(info.animator!!)
+ }
+
+ suspend private fun waitUntilComplete(animator: ValueAnimator) {
+ withContext(Dispatchers.Main) {
+ val startTime = System.currentTimeMillis()
+ while (!isTerminated && animator.isRunning()) {
+ delay(1)
+ if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) {
+ fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION")
+ }
+ }
+
+ animator.getAnimationHandler().setProvider(null)
+ }
+
+ job?.cancel()
+ }
+
+ override fun postFrameCallback(cb: FrameCallback) {
+ frames.value = Pair(frameCount++, cb)
+ }
+ override fun postCommitCallback(runnable: Runnable) {}
+ override fun getFrameTime() = frameCount
+ override fun getFrameDelay() = 1L
+ override fun setFrameDelay(delay: Long) {}
+
+ companion object {
+ private const val MAX_TEST_DURATION = 100L
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS
new file mode 100644
index 000000000000..7ccb316dbca5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 1254381
+azappone@google.com
+achalke@google.com
+juliacr@google.com
+madym@google.com
+mgalhardo@google.com
+petrcermak@google.com
+vanjan@google.com \ No newline at end of file
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 caf8321949ca..5058373e39b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -226,7 +226,8 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
+ " " + mockTileViewString + "\n"
+ " media bounds: null\n"
+ " horizontal layout: false\n"
- + " last orientation: 0\n";
+ + " last orientation: 0\n"
+ + " mShouldUseSplitNotificationShade: false\n";
assertEquals(expected, w.getBuffer().toString());
}
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 5e082f686ea3..6cf642cb7fd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -135,10 +135,10 @@ class QSPanelControllerTest : SysuiTestCase() {
fun configurationChange_onlySplitShadeConfigChanges_tileAreRedistributed() {
testableResources.addOverride(R.bool.config_use_split_notification_shade, false)
controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
- verify(pagedTileLayout, never()).forceTilesRedistribution()
+ verify(pagedTileLayout, never()).forceTilesRedistribution(any())
testableResources.addOverride(R.bool.config_use_split_notification_shade, true)
controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
- verify(pagedTileLayout).forceTilesRedistribution()
+ verify(pagedTileLayout).forceTilesRedistribution("Split shade state changed")
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 7c930b196d68..d52b29642acf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -27,6 +27,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSIconViewImpl
import com.android.systemui.qs.tileimpl.QSTileViewImpl
import com.google.common.truth.Truth.assertThat
@@ -34,6 +35,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -42,6 +44,9 @@ import org.mockito.MockitoAnnotations
@RunWithLooper
@SmallTest
class QSPanelTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsLogger: QSLogger
+
private lateinit var testableLooper: TestableLooper
private lateinit var qsPanel: QSPanel
@@ -57,7 +62,7 @@ class QSPanelTest : SysuiTestCase() {
qsPanel = QSPanel(context, null)
qsPanel.mUsingMediaPlayer = true
- qsPanel.initialize()
+ qsPanel.initialize(qsLogger)
// QSPanel inflates a footer inside of it, mocking it here
footer = LinearLayout(context).apply { id = R.id.qs_footer }
qsPanel.addView(footer, MATCH_PARENT, 100)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt
index a6a584d2e622..3fba3938db19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt
@@ -7,10 +7,12 @@ import android.view.accessibility.AccessibilityNodeInfo
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.logging.QSLogger
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
@@ -19,6 +21,8 @@ import org.mockito.MockitoAnnotations
@SmallTest
class QuickQSPanelTest : SysuiTestCase() {
+ @Mock private lateinit var qsLogger: QSLogger
+
private lateinit var testableLooper: TestableLooper
private lateinit var quickQSPanel: QuickQSPanel
@@ -32,7 +36,7 @@ class QuickQSPanelTest : SysuiTestCase() {
testableLooper.runWithLooper {
quickQSPanel = QuickQSPanel(mContext, null)
- quickQSPanel.initialize()
+ quickQSPanel.initialize(qsLogger)
quickQSPanel.onFinishInflate()
// Provides a parent with non-zero size for QSPanel
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 6a7308c0a96b..e3a6b29d2faf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -1684,6 +1684,42 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
assertThat(mNotificationPanelViewController.isFullyExpanded()).isTrue();
}
+ @Test
+ public void shadeExpanded_inShadeState() {
+ mStatusBarStateController.setState(SHADE);
+
+ mNotificationPanelViewController.setExpandedHeight(0);
+ assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isFalse();
+
+ int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
+ mNotificationPanelViewController.setExpandedHeight(transitionDistance);
+ assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isTrue();
+ }
+
+ @Test
+ public void shadeExpanded_onKeyguard() {
+ mStatusBarStateController.setState(KEYGUARD);
+
+ int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
+ mNotificationPanelViewController.setExpandedHeight(transitionDistance);
+ assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isFalse();
+
+ // set maxQsExpansion in NPVC
+ int maxQsExpansion = 123;
+ mNotificationPanelViewController.setQs(mQs);
+ when(mQs.getDesiredHeight()).thenReturn(maxQsExpansion);
+ triggerLayoutChange();
+
+ mNotificationPanelViewController.setQsExpansionHeight(maxQsExpansion);
+ assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isTrue();
+ }
+
+ @Test
+ public void shadeExpanded_onShadeLocked() {
+ mStatusBarStateController.setState(SHADE_LOCKED);
+ assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isTrue();
+ }
+
private static MotionEvent createMotionEvent(int x, int y, int action) {
return MotionEvent.obtain(
/* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0);
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 5394e6b2819b..0dac9a2be760 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -675,6 +675,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
String message = mContext.getString(R.string.keyguard_retry);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(false);
when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true);
mController.setVisible(true);
@@ -685,6 +686,21 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
+ public void transientIndication_swipeUpToRetry_faceAuthenticated() {
+ createController();
+ String message = mContext.getString(R.string.keyguard_retry);
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true);
+
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
+ "A message", BiometricSourceType.FACE);
+
+ verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(eq(message), any());
+ }
+
+ @Test
public void faceErrorTimeout_whenFingerprintEnrolled_doesNotShowMessage() {
createController();
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
@@ -1060,7 +1076,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// GIVEN a trust granted message but trust isn't granted
final String trustGrantedMsg = "testing trust granted message";
mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
- false, new TrustGrantFlags(0), trustGrantedMsg);
+ false, false, new TrustGrantFlags(0), trustGrantedMsg);
verifyHideIndication(INDICATION_TYPE_TRUST);
@@ -1085,7 +1101,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// WHEN the showTrustGranted method is called
final String trustGrantedMsg = "testing trust granted message";
mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
- false, new TrustGrantFlags(0), trustGrantedMsg);
+ false, false, new TrustGrantFlags(0), trustGrantedMsg);
// THEN verify the trust granted message shows
verifyIndicationMessage(
@@ -1103,7 +1119,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// WHEN the showTrustGranted method is called with a null message
mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
- false, new TrustGrantFlags(0), null);
+ false, false, new TrustGrantFlags(0), null);
// THEN verify the default trust granted message shows
verifyIndicationMessage(
@@ -1121,7 +1137,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// WHEN the showTrustGranted method is called with an EMPTY string
mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
- false, new TrustGrantFlags(0), "");
+ false, false, new TrustGrantFlags(0), "");
// THEN verify NO trust message is shown
verifyNoMessage(INDICATION_TYPE_TRUST);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt
new file mode 100644
index 000000000000..a6a9e51aa555
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt
@@ -0,0 +1,210 @@
+/*
+ * 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.fsi
+
+import android.R
+import android.app.Notification
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.service.dreams.IDreamManager
+import android.service.notification.StatusBarNotification
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import java.util.concurrent.Executor
+import junit.framework.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class FsiChromeRepoTest : SysuiTestCase() {
+
+ @Mock lateinit var centralSurfaces: CentralSurfaces
+ @Mock lateinit var fsiChromeRepo: FsiChromeRepo
+ @Mock lateinit var packageManager: PackageManager
+
+ var keyguardRepo = FakeKeyguardRepository()
+ @Mock private lateinit var applicationInfo: ApplicationInfo
+
+ @Mock lateinit var launchFullScreenIntentProvider: LaunchFullScreenIntentProvider
+ var featureFlags = FakeFeatureFlags()
+ @Mock lateinit var dreamManager: IDreamManager
+
+ // Execute all foreground & background requests immediately
+ private val uiBgExecutor = Executor { r -> r.run() }
+
+ private val appName: String = "appName"
+ private val appIcon: Drawable = context.getDrawable(com.android.systemui.R.drawable.ic_android)
+ private val fsi: PendingIntent = Mockito.mock(PendingIntent::class.java)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ // Set up package manager mocks
+ whenever(packageManager.getApplicationIcon(anyString())).thenReturn(appIcon)
+ whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
+ .thenReturn(appIcon)
+ whenever(packageManager.getApplicationLabel(any())).thenReturn(appName)
+ mContext.setMockPackageManager(packageManager)
+
+ fsiChromeRepo =
+ FsiChromeRepo(
+ mContext,
+ packageManager,
+ keyguardRepo,
+ launchFullScreenIntentProvider,
+ featureFlags,
+ uiBgExecutor,
+ dreamManager,
+ centralSurfaces
+ )
+ }
+
+ private fun createFsiEntry(fsi: PendingIntent): NotificationEntry {
+ val nb =
+ Notification.Builder(mContext, "a")
+ .setContentTitle("foo")
+ .setSmallIcon(R.drawable.sym_def_app_icon)
+ .setFullScreenIntent(fsi, /* highPriority= */ true)
+
+ val sbn =
+ StatusBarNotification(
+ "pkg",
+ "opPkg",
+ /* id= */ 0,
+ "tag" + System.currentTimeMillis(),
+ /* uid= */ 0,
+ /* initialPid */ 0,
+ nb.build(),
+ UserHandle(0),
+ /* overrideGroupKey= */ null,
+ /* postTime= */ 0
+ )
+
+ val entry = Mockito.mock(NotificationEntry::class.java)
+ whenever(entry.importance).thenReturn(NotificationManager.IMPORTANCE_HIGH)
+ whenever(entry.sbn).thenReturn(sbn)
+ return entry
+ }
+
+ @Test
+ fun testLaunchFullscreenIntent_flagNotEnabled_noLaunch() {
+ // Setup
+ featureFlags.set(Flags.FSI_CHROME, false)
+
+ // Test
+ val entry = createFsiEntry(fsi)
+ fsiChromeRepo.launchFullscreenIntent(entry)
+
+ // Verify
+ Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent()
+ }
+
+ @Test
+ fun testLaunchFullscreenIntent_notOnKeyguard_noLaunch() {
+ // Setup
+ featureFlags.set(Flags.FSI_CHROME, true)
+ keyguardRepo.setKeyguardShowing(false)
+
+ // Test
+ val entry = createFsiEntry(fsi)
+ fsiChromeRepo.launchFullscreenIntent(entry)
+
+ // Verify
+ Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent()
+ }
+
+ @Test
+ fun testLaunchFullscreenIntent_stopsScreensaver() {
+ // Setup
+ featureFlags.set(Flags.FSI_CHROME, true)
+ keyguardRepo.setKeyguardShowing(true)
+
+ // Test
+ val entry = createFsiEntry(fsi)
+ fsiChromeRepo.launchFullscreenIntent(entry)
+
+ // Verify
+ Mockito.verify(dreamManager, times(1)).awaken()
+ }
+
+ @Test
+ fun testLaunchFullscreenIntent_updatesFsiInfoFlow() {
+ // Setup
+ featureFlags.set(Flags.FSI_CHROME, true)
+ keyguardRepo.setKeyguardShowing(true)
+
+ // Test
+ val entry = createFsiEntry(fsi)
+ fsiChromeRepo.launchFullscreenIntent(entry)
+
+ // Verify
+ val expectedFsiInfo = FsiChromeRepo.FSIInfo(appName, appIcon, fsi)
+ assertEquals(expectedFsiInfo, fsiChromeRepo.infoFlow.value)
+ }
+
+ @Test
+ fun testLaunchFullscreenIntent_notifyFsiLaunched() {
+ // Setup
+ featureFlags.set(Flags.FSI_CHROME, true)
+ keyguardRepo.setKeyguardShowing(true)
+
+ // Test
+ val entry = createFsiEntry(fsi)
+ fsiChromeRepo.launchFullscreenIntent(entry)
+
+ // Verify
+ Mockito.verify(entry, times(1)).notifyFullScreenIntentLaunched()
+ }
+
+ @Test
+ fun testLaunchFullscreenIntent_wakesUpDevice() {
+ // Setup
+ featureFlags.set(Flags.FSI_CHROME, true)
+ keyguardRepo.setKeyguardShowing(true)
+
+ // Test
+ val entry = createFsiEntry(fsi)
+ fsiChromeRepo.launchFullscreenIntent(entry)
+
+ // Verify
+ Mockito.verify(centralSurfaces, times(1)).wakeUpForFullScreenIntent()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
index 5f5769572008..3f61af0425de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.FakeShadowView
import com.android.systemui.statusbar.notification.NotificationUtils
+import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -83,4 +84,17 @@ class ActivatableNotificationViewTest : SysuiTestCase() {
mView.updateBackgroundColors()
assertThat(mView.currentBackgroundTint).isEqualTo(mNormalColor)
}
+
+ @Test
+ fun roundnessShouldBeTheSame_after_onDensityOrFontScaleChanged() {
+ val roundableState = mView.roundableState
+ assertThat(mView.topRoundness).isEqualTo(0f)
+ mView.requestTopRoundness(1f, SourceType.from(""))
+ assertThat(mView.topRoundness).isEqualTo(1f)
+
+ mView.onDensityOrFontScaleChanged()
+
+ assertThat(mView.topRoundness).isEqualTo(1f)
+ assertThat(mView.roundableState.hashCode()).isEqualTo(roundableState.hashCode())
+ }
} \ No newline at end of file
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 1503392eccf2..1ce460c60a54 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
@@ -19,6 +19,9 @@ package com.android.systemui.statusbar.phone;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertFalse;
@@ -835,7 +838,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true);
- mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(SHADE);
try {
mCentralSurfaces.handleVisibleToUserChanged(true);
@@ -854,7 +857,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
- mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(SHADE);
try {
mCentralSurfaces.handleVisibleToUserChanged(true);
@@ -995,7 +998,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
public void testShowKeyguardImplementation_setsState() {
when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
- mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(SHADE);
// By default, showKeyguardImpl sets state to KEYGUARD.
mCentralSurfaces.showKeyguardImpl();
@@ -1052,7 +1055,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
public void collapseShade_callsanimateCollapseShade_whenExpanded() {
// GIVEN the shade is expanded
mCentralSurfaces.onShadeExpansionFullyChanged(true);
- mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(SHADE);
// WHEN collapseShade is called
mCentralSurfaces.collapseShade();
@@ -1065,7 +1068,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
public void collapseShade_doesNotCallanimateCollapseShade_whenCollapsed() {
// GIVEN the shade is collapsed
mCentralSurfaces.onShadeExpansionFullyChanged(false);
- mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(SHADE);
// WHEN collapseShade is called
mCentralSurfaces.collapseShade();
@@ -1078,7 +1081,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
public void collapseShadeForBugReport_callsanimateCollapseShade_whenFlagDisabled() {
// GIVEN the shade is expanded & flag enabled
mCentralSurfaces.onShadeExpansionFullyChanged(true);
- mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(SHADE);
mFeatureFlags.set(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, false);
// WHEN collapseShadeForBugreport is called
@@ -1092,7 +1095,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
public void collapseShadeForBugReport_doesNotCallanimateCollapseShade_whenFlagEnabled() {
// GIVEN the shade is expanded & flag enabled
mCentralSurfaces.onShadeExpansionFullyChanged(true);
- mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(SHADE);
mFeatureFlags.set(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, true);
// WHEN collapseShadeForBugreport is called
@@ -1104,10 +1107,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
- when(mKeyguardStateController.isShowing()).thenReturn(false);
setFoldedStates(FOLD_STATE_FOLDED);
setGoToSleepStates(FOLD_STATE_FOLDED);
- when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
+ mCentralSurfaces.setBarStateForTest(SHADE);
+ when(mNotificationPanelViewController.isShadeFullyOpen()).thenReturn(true);
setDeviceState(FOLD_STATE_UNFOLDED);
@@ -1116,10 +1119,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void deviceStateChange_unfolded_shadeOpen_onKeyguard_doesNotSetLeaveOpenOnKeyguardHide() {
- when(mKeyguardStateController.isShowing()).thenReturn(true);
setFoldedStates(FOLD_STATE_FOLDED);
setGoToSleepStates(FOLD_STATE_FOLDED);
- when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
+ mCentralSurfaces.setBarStateForTest(KEYGUARD);
+ when(mNotificationPanelViewController.isShadeFullyOpen()).thenReturn(true);
setDeviceState(FOLD_STATE_UNFOLDED);
@@ -1131,7 +1134,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() {
setFoldedStates(FOLD_STATE_FOLDED);
setGoToSleepStates(FOLD_STATE_FOLDED);
- when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(false);
+ mCentralSurfaces.setBarStateForTest(SHADE);
+ when(mNotificationPanelViewController.isShadeFullyOpen()).thenReturn(false);
setDeviceState(FOLD_STATE_UNFOLDED);
@@ -1165,12 +1169,12 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
// it to remain visible.
when(mKeyguardViewMediator.isOccludeAnimationPlaying()).thenReturn(true);
setKeyguardShowingAndOccluded(false /* showing */, true /* occluded */);
- verify(mStatusBarStateController, never()).setState(StatusBarState.SHADE);
+ verify(mStatusBarStateController, never()).setState(SHADE);
// Once the animation ends, verify that the keyguard is actually hidden.
when(mKeyguardViewMediator.isOccludeAnimationPlaying()).thenReturn(false);
setKeyguardShowingAndOccluded(false /* showing */, true /* occluded */);
- verify(mStatusBarStateController).setState(StatusBarState.SHADE);
+ verify(mStatusBarStateController).setState(SHADE);
}
@Test
@@ -1183,7 +1187,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
// immediately hide the keyguard.
when(mKeyguardViewMediator.isOccludeAnimationPlaying()).thenReturn(false);
setKeyguardShowingAndOccluded(false /* showing */, true /* occluded */);
- verify(mStatusBarStateController).setState(StatusBarState.SHADE);
+ verify(mStatusBarStateController).setState(SHADE);
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index cae414a3dc67..19658e6398c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -55,6 +55,7 @@ import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -220,7 +221,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mock(NotificationPanelViewController.class),
mActivityLaunchAnimator,
notificationAnimationProvider,
- mock(LaunchFullScreenIntentProvider.class)
+ mock(LaunchFullScreenIntentProvider.class),
+ mock(FeatureFlags.class)
);
// set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS
new file mode 100644
index 000000000000..7ccb316dbca5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 1254381
+azappone@google.com
+achalke@google.com
+juliacr@google.com
+madym@google.com
+mgalhardo@google.com
+petrcermak@google.com
+vanjan@google.com \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index d31f0bbf49af..2aed831211ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -1332,10 +1332,10 @@ public class BubblesTest extends SysuiTestCase {
mBubbleController.updateBubble(mBubbleEntry);
verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
mFilterArgumentCaptor.capture(), eq(Context.RECEIVER_EXPORTED));
- assertThat(mFilterArgumentCaptor.getValue().getAction(0)).isEqualTo(
- Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- assertThat(mFilterArgumentCaptor.getValue().getAction(1)).isEqualTo(
- Intent.ACTION_SCREEN_OFF);
+ assertThat(mFilterArgumentCaptor.getValue()
+ .hasAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)).isTrue();
+ assertThat(mFilterArgumentCaptor.getValue()
+ .hasAction(Intent.ACTION_SCREEN_OFF)).isTrue();
mBubbleData.dismissBubbleWithKey(mBubbleEntry.getKey(), REASON_APP_CANCEL);
// TODO: not certain why this isn't called normally when tests are run, perhaps because
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 5c2a915e81b6..55019490bdcd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -120,6 +120,14 @@ class FakeKeyguardRepository : KeyguardRepository {
_dozeAmount.value = dozeAmount
}
+ fun setWakefulnessModel(model: WakefulnessModel) {
+ _wakefulnessModel.value = model
+ }
+
+ fun setBouncerShowing(isShowing: Boolean) {
+ _isBouncerShowing.value = isShowing
+ }
+
fun setBiometricUnlockState(state: BiometricUnlockModel) {
_biometricUnlockState.tryEmit(state)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
new file mode 100644
index 000000000000..2c0a8fde0d77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.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.systemui.shade.data.repository
+
+import com.android.systemui.shade.domain.model.ShadeModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [KeyguardRepository] */
+class FakeShadeRepository : ShadeRepository {
+
+ private val _shadeModel = MutableStateFlow(ShadeModel())
+ override val shadeModel: Flow<ShadeModel> = _shadeModel
+
+ fun setShadeModel(model: ShadeModel) {
+ _shadeModel.value = model
+ }
+}
diff --git a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
index 3270744783e6..eaa0aeffcb3e 100644
--- a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
+++ b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
@@ -16,24 +16,24 @@
<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">"Zahtev za povezivanje"</string>
- <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja omogućava praćenje saobraćaja na mreži. Prihvatite samo ako verujete izvoru. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se prikazuje u vrhu ekrana kada je VPN aktivan."</string>
- <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja joj omogućava da prati mrežni saobraćaj. Prihvatite ovo samo ako imate poverenja u izvor. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se prikazuje na ekranu kada je VPN aktivan."</string>
- <string name="legacy_title" msgid="192936250066580964">"VPN je povezan"</string>
- <string name="session" msgid="6470628549473641030">"Sesija:"</string>
- <string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
- <string name="data_transmitted" msgid="7988167672982199061">"Poslato:"</string>
- <string name="data_received" msgid="4062776929376067820">"Primljeno:"</string>
- <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> bajt(ov)a / <xliff:g id="NUMBER_1">%2$s</xliff:g> paketa"</string>
- <string name="always_on_disconnected_title" msgid="1906740176262776166">"Povezivanje sa uvek uključenim VPN-om nije uspelo"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"Mreža <xliff:g id="VPN_APP_0">%1$s</xliff:g> je podešena da bude uvek povezana, ali trenutno ne može da uspostavi vezu. Telefon će koristiti javnu mrežu dok se ponovo ne poveže sa <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
- <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"Mreža <xliff:g id="VPN_APP">%1$s</xliff:g> je podešena da bude uvek povezana, ali trenutno ne može da uspostavi vezu. Nećete imati vezu dok se VPN ponovo ne poveže."</string>
+ <string name="prompt" msgid="3183836924226407828">"Захтев за повезивање"</string>
+ <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која омогућава праћење саобраћаја на мрежи. Прихватите само ако верујете извору. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се приказује у врху екрана када је VPN активан."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Апликација <xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која јој омогућава да прати мрежни саобраћај. Прихватите ово само ако имате поверења у извор. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се приказује на екрану када је VPN активан."</string>
+ <string name="legacy_title" msgid="192936250066580964">"VPN је повезан"</string>
+ <string name="session" msgid="6470628549473641030">"Сесија:"</string>
+ <string name="duration" msgid="3584782459928719435">"Трајање:"</string>
+ <string name="data_transmitted" msgid="7988167672982199061">"Послато:"</string>
+ <string name="data_received" msgid="4062776929376067820">"Примљенo:"</string>
+ <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> бајт(ов)а / <xliff:g id="NUMBER_1">%2$s</xliff:g> пакета"</string>
+ <string name="always_on_disconnected_title" msgid="1906740176262776166">"Повезивање са увек укљученим VPN-ом није успело"</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"Мрежа <xliff:g id="VPN_APP_0">%1$s</xliff:g> је подешена да буде увек повезана, али тренутно не може да успостави везу. Телефон ће користити јавну мрежу док се поново не повеже са <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
+ <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"Мрежа <xliff:g id="VPN_APP">%1$s</xliff:g> је подешена да буде увек повезана, али тренутно не може да успостави везу. Нећете имати везу док се VPN поново не повеже."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
- <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Promeni podešavanja VPN-a"</string>
- <string name="configure" msgid="4905518375574791375">"Konfiguriši"</string>
- <string name="disconnect" msgid="971412338304200056">"Prekini vezu"</string>
- <string name="open_app" msgid="3717639178595958667">"Otvori aplikaciju"</string>
- <string name="dismiss" msgid="6192859333764711227">"Odbaci"</string>
+ <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Промени подешавања VPN-а"</string>
+ <string name="configure" msgid="4905518375574791375">"Конфигуриши"</string>
+ <string name="disconnect" msgid="971412338304200056">"Прекини везу"</string>
+ <string name="open_app" msgid="3717639178595958667">"Отвори апликацију"</string>
+ <string name="dismiss" msgid="6192859333764711227">"Одбаци"</string>
<string name="sanitized_vpn_label_with_ellipsis" msgid="7014327474633422235">"<xliff:g id="SANITIZED_VPN_LABEL_WITH_ELLIPSIS_0">%1$s</xliff:g>… ( <xliff:g id="SANITIZED_VPN_LABEL_WITH_ELLIPSIS_1">%2$s</xliff:g>)"</string>
<string name="sanitized_vpn_label" msgid="1877415015009794766">"<xliff:g id="SANITIZED_VPN_LABEL_0">%1$s</xliff:g> ( <xliff:g id="SANITIZED_VPN_LABEL_1">%2$s</xliff:g>)"</string>
</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
index f80fa8d14054..c2b611e70da9 100644
--- a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/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">"Prikazuj aplikacije ispod oblasti izreza"</string>
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Приказуј апликације испод области изреза"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-b+sr+Latn/strings.xml
index 5393410cffb6..0adf1dda219f 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-b+sr+Latn/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-b+sr+Latn/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="1677693377327336341">"Izrezana slika u uglu ekrana"</string>
+ <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"Изрезана слика у углу екрана"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-b+sr+Latn/strings.xml
index 97d544c56568..411ab7ab85f5 100644
--- a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-b+sr+Latn/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-b+sr+Latn/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="7305489596221077240">"Izrezana slika u obliku rupe"</string>
+ <string name="display_cutout_emulation_overlay" msgid="7305489596221077240">"Изрезана слика у облику рупе"</string>
</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
index 082e5864065f..26afbf962495 100644
--- a/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/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">"Sakrij"</string>
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сакриј"</string>
</resources>
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 6bb19ce05813..5f1da7b18bf9 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -201,6 +201,30 @@ public final class PresentationStatsEventLogger {
});
}
+ public void maybeSetFillRequestSentTimestampMs(int timestamp) {
+ mEventInternal.ifPresent(event -> {
+ event.mFillRequestSentTimestampMs = timestamp;
+ });
+ }
+
+ public void maybeSetFillResponseReceivedTimestampMs(int timestamp) {
+ mEventInternal.ifPresent(event -> {
+ event.mFillResponseReceivedTimestampMs = timestamp;
+ });
+ }
+
+ public void maybeSetSuggestionSentTimestampMs(int timestamp) {
+ mEventInternal.ifPresent(event -> {
+ event.mSuggestionSentTimestampMs = timestamp;
+ });
+ }
+
+ public void maybeSetSuggestionPresentedTimestampMs(int timestamp) {
+ mEventInternal.ifPresent(event -> {
+ event.mSuggestionPresentedTimestampMs = timestamp;
+ });
+ }
+
public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
mEventInternal.ifPresent(event -> {
event.mDisplayPresentationType =
@@ -262,7 +286,11 @@ public final class PresentationStatsEventLogger {
+ " mDisplayPresentationType=" + event.mDisplayPresentationType
+ " mAutofillServiceUid=" + event.mAutofillServiceUid
+ " mInlineSuggestionHostUid=" + event.mInlineSuggestionHostUid
- + " mIsRequestTriggered=" + event.mIsRequestTriggered);
+ + " mIsRequestTriggered=" + event.mIsRequestTriggered
+ + " mFillRequestSentTimestampMs=" + event.mFillRequestSentTimestampMs
+ + " mFillResponseReceivedTimestampMs=" + event.mFillResponseReceivedTimestampMs
+ + " mSuggestionSentTimestampMs=" + event.mSuggestionSentTimestampMs
+ + " mSuggestionPresentedTimestampMs=" + event.mSuggestionPresentedTimestampMs);
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -283,7 +311,11 @@ public final class PresentationStatsEventLogger {
event.mDisplayPresentationType,
event.mAutofillServiceUid,
event.mInlineSuggestionHostUid,
- event.mIsRequestTriggered);
+ event.mIsRequestTriggered,
+ event.mFillRequestSentTimestampMs,
+ event.mFillResponseReceivedTimestampMs,
+ event.mSuggestionSentTimestampMs,
+ event.mSuggestionPresentedTimestampMs);
mEventInternal = Optional.empty();
}
@@ -300,6 +332,10 @@ public final class PresentationStatsEventLogger {
int mAutofillServiceUid = -1;
int mInlineSuggestionHostUid = -1;
boolean mIsRequestTriggered;
+ int mFillRequestSentTimestampMs;
+ int mFillResponseReceivedTimestampMs;
+ int mSuggestionSentTimestampMs;
+ int mSuggestionPresentedTimestampMs;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 64b7688cc196..b3f8af533814 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -324,6 +324,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private final long mStartTime;
/**
+ * Starting timestamp of latency logger.
+ * This is set when Session created or when the view is reset.
+ */
+ @GuardedBy("mLock")
+ private long mLatencyBaseTime;
+
+ /**
* When the UI was shown for the first time (using elapsed time since boot).
*/
@GuardedBy("mLock")
@@ -1041,6 +1048,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
+ final long fillRequestSentRelativeTimestamp =
+ SystemClock.elapsedRealtime() - mLatencyBaseTime;
+ mPresentationStatsEventLogger.maybeSetFillRequestSentTimestampMs(
+ (int) (fillRequestSentRelativeTimestamp));
+
// Now request the assist structure data.
requestAssistStructureLocked(requestId, flags);
}
@@ -1085,6 +1097,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
this.taskId = taskId;
this.uid = uid;
mStartTime = SystemClock.elapsedRealtime();
+ mLatencyBaseTime = mStartTime;
mService = service;
mLock = lock;
mUi = ui;
@@ -1116,6 +1129,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public void notifyInlineUiShown(AutofillId autofillId) {
notifyFillUiShown(autofillId);
+
+ synchronized (mLock) {
+ // TODO(b/262448552): Log when chip inflates instead of here
+ final long inlineUiShownRelativeTimestamp =
+ SystemClock.elapsedRealtime() - mLatencyBaseTime;
+ mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs(
+ (int) (inlineUiShownRelativeTimestamp));
+ }
}
@Override
@@ -1209,6 +1230,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
+ // Time passed since session was created
+ final long fillRequestReceivedRelativeTimestamp =
+ SystemClock.elapsedRealtime() - mLatencyBaseTime;
+ mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs(
+ (int) (fillRequestReceivedRelativeTimestamp));
+
requestLog = mRequestLogs.get(requestId);
if (requestLog != null) {
requestLog.setType(MetricsEvent.TYPE_SUCCESS);
@@ -3128,6 +3155,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
break;
case ACTION_VIEW_ENTERED:
+ mLatencyBaseTime = SystemClock.elapsedRealtime();
boolean wasPreviouslyFillDialog = mPreviouslyFillDialogPotentiallyStarted;
mPreviouslyFillDialogPotentiallyStarted = false;
if (sVerbose && virtualBounds != null) {
@@ -3181,7 +3209,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
}
-
+ // If previous request was FillDialog request, a logger event was already started
if (!wasPreviouslyFillDialog) {
mPresentationStatsEventLogger.startNewEvent();
mPresentationStatsEventLogger.maybeSetAutofillServiceUid(
@@ -3405,6 +3433,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
+ synchronized (mLock) {
+ // Time passed since Session was created
+ long suggestionSentRelativeTimestamp =
+ SystemClock.elapsedRealtime() - mLatencyBaseTime;
+ mPresentationStatsEventLogger.maybeSetSuggestionSentTimestampMs(
+ (int) (suggestionSentRelativeTimestamp));
+ }
+
final AutofillId[] ids = response.getFillDialogTriggerIds();
if (ids != null && ArrayUtils.contains(ids, filledId)) {
if (requestShowFillDialog(response, filledId, filterText, flags)) {
@@ -3421,6 +3457,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Note: Cannot disable before requestShowFillDialog() because the method
// need to check whether fill dialog enabled.
setFillDialogDisabled();
+ synchronized (mLock) {
+ // Logs when fill dialog ui is shown; time since Session was created
+ final long fillDialogUiShownRelativeTimestamp =
+ SystemClock.elapsedRealtime() - mLatencyBaseTime;
+ mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs(
+ (int) (fillDialogUiShownRelativeTimestamp));
+ }
return;
} else {
setFillDialogDisabled();
@@ -3431,6 +3474,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (response.supportsInlineSuggestions()) {
synchronized (mLock) {
if (requestShowInlineSuggestionsLocked(response, filterText)) {
+ // Cannot tell for sure that InlineSuggestions are shown yet, IME needs to send
+ // back a response via callback.
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_INLINE_SHOWN);
// TODO(b/248378401): Fix it to log showed only when IME asks for inflation,
@@ -3464,6 +3509,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Log first time UI is shown.
mUiShownTime = SystemClock.elapsedRealtime();
final long duration = mUiShownTime - mStartTime;
+ // This logs when dropdown ui was shown. Timestamp is relative to
+ // when the session was created
+ mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs(
+ (int) (mUiShownTime - mLatencyBaseTime));
+
if (sDebug) {
final StringBuilder msg = new StringBuilder("1st UI for ")
.append(mActivityToken)
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 5e68d5231c6b..6b2e893a55c9 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -32,6 +32,7 @@ import android.companion.virtual.VirtualDeviceParams.RecentsPolicy;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Handler;
@@ -93,6 +94,12 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
void onEnteringPipBlocked(int uid);
}
+ /** Interface to listen for interception of intents. */
+ public interface IntentListenerCallback {
+ /** Returns true when an intent should be intercepted */
+ boolean shouldInterceptIntent(Intent intent);
+ }
+
/**
* If required, allow the secure activity to display on remote device since
* {@link android.os.Build.VERSION_CODES#TIRAMISU}.
@@ -121,6 +128,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
final ArraySet<Integer> mRunningUids = new ArraySet<>();
@Nullable private final ActivityListener mActivityListener;
@Nullable private final PipBlockedCallback mPipBlockedCallback;
+ @Nullable private final IntentListenerCallback mIntentListenerCallback;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
@@ -155,6 +163,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
* launching.
* @param secureWindowCallback Callback that is called when a secure window shows on the
* virtual display.
+ * @param intentListenerCallback Callback that is called to intercept intents when matching
+ * passed in filters.
* @param defaultRecentsPolicy a policy to indicate how to handle activities in recents.
*/
public GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
@@ -168,6 +178,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
@NonNull PipBlockedCallback pipBlockedCallback,
@NonNull ActivityBlockedCallback activityBlockedCallback,
@NonNull SecureWindowCallback secureWindowCallback,
+ @NonNull IntentListenerCallback intentListenerCallback,
@NonNull List<String> displayCategories,
@RecentsPolicy int defaultRecentsPolicy) {
super();
@@ -182,6 +193,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
mActivityListener = activityListener;
mPipBlockedCallback = pipBlockedCallback;
mSecureWindowCallback = secureWindowCallback;
+ mIntentListenerCallback = intentListenerCallback;
mDisplayCategories = displayCategories;
mDefaultRecentsPolicy = defaultRecentsPolicy;
}
@@ -227,8 +239,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
@Override
public boolean canActivityBeLaunched(ActivityInfo activityInfo,
- @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
- boolean isNewTask) {
+ Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
+ int launchingFromDisplayId, boolean isNewTask) {
if (!isWindowingModeSupported(windowingMode)) {
return false;
}
@@ -261,6 +273,12 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
return false;
}
+ if (mIntentListenerCallback != null && intent != null
+ && mIntentListenerCallback.shouldInterceptIntent(intent)) {
+ Slog.d(TAG, "Virtual device has intercepted intent");
+ return false;
+ }
+
return true;
}
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 97b5d6ddf6b6..d5cc58f3cd0c 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -142,17 +142,18 @@ class InputController {
}
}
- void createKeyboard(@NonNull String deviceName,
- int vendorId,
- int productId,
- @NonNull IBinder deviceToken,
- int displayId) {
+ void createKeyboard(@NonNull String deviceName, int vendorId, int productId,
+ @NonNull IBinder deviceToken, int displayId, @NonNull String languageTag,
+ @NonNull String layoutType) {
final String phys = createPhys(PHYS_TYPE_KEYBOARD);
+ mInputManagerInternal.addKeyboardLayoutAssociation(phys, languageTag,
+ layoutType);
try {
createDeviceInternal(InputDeviceDescriptor.TYPE_KEYBOARD, deviceName, vendorId,
productId, deviceToken, displayId, phys,
() -> mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys));
} catch (DeviceCreationException e) {
+ mInputManagerInternal.removeKeyboardLayoutAssociation(phys);
throw new RuntimeException(
"Failed to create virtual keyboard device '" + deviceName + "'.", e);
}
@@ -233,12 +234,16 @@ class InputController {
InputDeviceDescriptor inputDeviceDescriptor) {
token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
-
- InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys());
+ String phys = inputDeviceDescriptor.getPhys();
+ InputManager.getInstance().removeUniqueIdAssociation(phys);
// Type associations are added in the case of navigation touchpads. Those should be removed
// once the input device gets closed.
if (inputDeviceDescriptor.getType() == InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD) {
- mInputManagerInternal.unsetTypeAssociation(inputDeviceDescriptor.getPhys());
+ mInputManagerInternal.unsetTypeAssociation(phys);
+ }
+
+ if (inputDeviceDescriptor.getType() == InputDeviceDescriptor.TYPE_KEYBOARD) {
+ mInputManagerInternal.removeKeyboardLayoutAssociation(phys);
}
// Reset values to the default if all virtual mice are unregistered, or set display
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 12ad9f1cf580..b6cd160af80e 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -33,6 +33,7 @@ import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
+import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.companion.virtual.VirtualDeviceParams;
@@ -43,6 +44,7 @@ import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.graphics.PointF;
import android.hardware.display.DisplayManager;
@@ -114,6 +116,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final VirtualDeviceParams mParams;
private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>();
private final IVirtualDeviceActivityListener mActivityListener;
+ @GuardedBy("mVirtualDeviceLock")
+ private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>();
@NonNull
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
// The default setting for showing the pointer on new displays.
@@ -435,7 +439,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
final long ident = Binder.clearCallingIdentity();
try {
mInputController.createKeyboard(config.getInputDeviceName(), config.getVendorId(),
- config.getProductId(), deviceToken, config.getAssociatedDisplayId());
+ config.getProductId(), deviceToken, config.getAssociatedDisplayId(),
+ config.getLanguageTag(), config.getLayoutType());
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -680,6 +685,31 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
}
+ @Override // Binder call
+ public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor,
+ IntentFilter filter) {
+ Objects.requireNonNull(intentInterceptor);
+ Objects.requireNonNull(filter);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "Permission required to register intent interceptor");
+ synchronized (mVirtualDeviceLock) {
+ mIntentInterceptors.put(intentInterceptor.asBinder(), filter);
+ }
+ }
+
+ @Override // Binder call
+ public void unregisterIntentInterceptor(
+ @NonNull IVirtualDeviceIntentInterceptor intentInterceptor) {
+ Objects.requireNonNull(intentInterceptor);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "Permission required to unregister intent interceptor");
+ synchronized (mVirtualDeviceLock) {
+ mIntentInterceptors.remove(intentInterceptor.asBinder());
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
fout.println(" VirtualDevice: ");
@@ -713,6 +743,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
this::onEnteringPipBlocked,
this::onActivityBlocked,
this::onSecureWindowShown,
+ this::shouldInterceptIntent,
displayCategories,
mParams.getDefaultRecentsPolicy());
gwpc.registerRunningAppsChangedListener(/* listener= */ this);
@@ -872,6 +903,34 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
Toast.LENGTH_LONG, mContext.getMainLooper());
}
+ /**
+ * Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true if
+ * the intent matches any filter notifying the DisplayPolicyController to abort the
+ * activity launch to be replaced by the interception.
+ */
+ private boolean shouldInterceptIntent(Intent intent) {
+ synchronized (mVirtualDeviceLock) {
+ boolean hasInterceptedIntent = false;
+ for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) {
+ if (interceptor.getValue().match(
+ intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(),
+ intent.getCategories(), TAG) >= 0) {
+ try {
+ // For privacy reasons, only returning the intents action and data. Any
+ // other required field will require a review.
+ IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey())
+ .onIntentIntercepted(new Intent(intent.getAction(), intent.getData()));
+ hasInterceptedIntent = true;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mVirtualDeviceIntentInterceptor", e);
+ }
+ }
+ }
+
+ return hasInterceptedIntent;
+ }
+ }
+
interface OnDeviceCloseListener {
void onClose(int deviceId);
}
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 17002d5b6e63..373080a6cff5 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -21,10 +21,12 @@ import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.apex.ApexInfo;
import android.apex.IApexService;
+import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
+import android.compat.annotation.ChangeId;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -126,6 +128,14 @@ public class BinaryTransparencyService extends SystemService {
// the system time (in ms) the last measurement was taken
private long mMeasurementsLastRecordedMs;
+ /**
+ * Guards whether or not measurements of MBA to be performed. When this change is enabled,
+ * measurements of MBAs are performed. But when it is disabled, only measurements of APEX
+ * and modules are done.
+ */
+ @ChangeId
+ public static final long LOG_MBA_INFO = 245692487L;
+
final class BinaryTransparencyServiceImpl extends IBinaryTransparencyService.Stub {
@Override
@@ -336,58 +346,63 @@ public class BinaryTransparencyService extends SystemService {
+ " packages after considering preloads");
}
- // lastly measure all newly installed MBAs
- for (PackageInfo packageInfo : getNewlyInstalledMbas()) {
- if (packagesMeasured.contains(packageInfo.packageName)) {
- continue;
- }
- packagesMeasured.add(packageInfo.packageName);
+ if (CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
+ // lastly measure all newly installed MBAs
+ for (PackageInfo packageInfo : getNewlyInstalledMbas()) {
+ if (packagesMeasured.contains(packageInfo.packageName)) {
+ continue;
+ }
+ packagesMeasured.add(packageInfo.packageName);
- Bundle packageMeasurement = measurePackage(packageInfo);
- results.add(packageMeasurement);
+ Bundle packageMeasurement = measurePackage(packageInfo);
+ results.add(packageMeasurement);
- if (record) {
- // compute digests of signing info
- String[] signerDigestHexStrings = computePackageSignerSha256Digests(
- packageInfo.signingInfo);
+ if (record) {
+ // compute digests of signing info
+ String[] signerDigestHexStrings = computePackageSignerSha256Digests(
+ packageInfo.signingInfo);
- // then extract package's InstallSourceInfo
- if (DEBUG) {
- Slog.d(TAG, "Extracting InstallSourceInfo for " + packageInfo.packageName);
- }
- InstallSourceInfo installSourceInfo = getInstallSourceInfo(
- packageInfo.packageName);
- String initiator = null;
- SigningInfo initiatorSignerInfo = null;
- String[] initiatorSignerInfoDigest = null;
- String installer = null;
- String originator = null;
-
- if (installSourceInfo != null) {
- initiator = installSourceInfo.getInitiatingPackageName();
- initiatorSignerInfo = installSourceInfo.getInitiatingPackageSigningInfo();
- if (initiatorSignerInfo != null) {
- initiatorSignerInfoDigest = computePackageSignerSha256Digests(
- initiatorSignerInfo);
+ // then extract package's InstallSourceInfo
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Extracting InstallSourceInfo for " + packageInfo.packageName);
+ }
+ InstallSourceInfo installSourceInfo = getInstallSourceInfo(
+ packageInfo.packageName);
+ String initiator = null;
+ SigningInfo initiatorSignerInfo = null;
+ String[] initiatorSignerInfoDigest = null;
+ String installer = null;
+ String originator = null;
+
+ if (installSourceInfo != null) {
+ initiator = installSourceInfo.getInitiatingPackageName();
+ initiatorSignerInfo =
+ installSourceInfo.getInitiatingPackageSigningInfo();
+ if (initiatorSignerInfo != null) {
+ initiatorSignerInfoDigest = computePackageSignerSha256Digests(
+ initiatorSignerInfo);
+ }
+ installer = installSourceInfo.getInstallingPackageName();
+ originator = installSourceInfo.getOriginatingPackageName();
}
- installer = installSourceInfo.getInstallingPackageName();
- originator = installSourceInfo.getOriginatingPackageName();
- }
- // we should now have all the info needed for the atom
- byte[] cDigest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
- FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
- packageInfo.packageName,
- packageInfo.getLongVersionCode(),
- (cDigest != null) ? HexEncoding.encodeToString(cDigest, false) : null,
- packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM),
- signerDigestHexStrings,
- MBA_STATUS_NEW_INSTALL, // mba_status
- initiator,
- initiatorSignerInfoDigest,
- installer,
- originator
- );
+ // we should now have all the info needed for the atom
+ byte[] cDigest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
+ FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
+ packageInfo.packageName,
+ packageInfo.getLongVersionCode(),
+ (cDigest != null) ? HexEncoding.encodeToString(cDigest, false)
+ : null,
+ packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM),
+ signerDigestHexStrings,
+ MBA_STATUS_NEW_INSTALL, // mba_status
+ initiator,
+ initiatorSignerInfoDigest,
+ installer,
+ originator
+ );
+ }
}
}
if (DEBUG) {
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 7d2e2766fd0b..f19f7f212513 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -79,7 +79,7 @@ public class GestureLauncherService extends SystemService {
* completed faster than this, we assume it's not performed by human and the
* event gets ignored.
*/
- @VisibleForTesting static final int EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS = 160;
+ @VisibleForTesting static final int EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS = 200;
/**
* Interval in milliseconds in which the power button must be depressed in succession to be
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f72321c5d9ac..87daa6d35a82 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3063,6 +3063,9 @@ public final class ActiveServices {
Slog.e(TAG_SERVICE, message);
}
mAm.appNotResponding(sr.app, tr);
+
+ // TODO(short-service): Make sure, if the FGS stops after this, the ANR dialog
+ // disappears.
}
}
@@ -3869,8 +3872,8 @@ public final class ActiveServices {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, "
+ className + " is not an isolatedProcess");
}
- if (AppGlobals.getPackageManager().getPackageUid(callingPackage,
- 0, userId) != callingUid) {
+ if (!mAm.getPackageManagerInternal().isSameApp(callingPackage, callingUid,
+ userId)) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, "
+ "calling package not owned by calling UID ");
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 045c757767e4..ccaf353607b1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9159,6 +9159,10 @@ public class ActivityManagerService extends IActivityManager.Stub
if (Debug.isDebuggerConnected()) {
sb.append("Debugger: Connected\n");
}
+ if (crashInfo != null && crashInfo.exceptionHandlerClassName != null
+ && !crashInfo.exceptionHandlerClassName.isEmpty()) {
+ sb.append("Crash-Handler: ").append(crashInfo.exceptionHandlerClassName).append("\n");
+ }
if (crashInfo != null && crashInfo.crashTag != null && !crashInfo.crashTag.isEmpty()) {
sb.append("Crash-Tag: ").append(crashInfo.crashTag).append("\n");
}
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
index ac25f4edfdb7..ef0de18ee3ef 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
@@ -218,13 +218,13 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
}
@Override
- public boolean arePackageModesDefault(String packageMode, @UserIdInt int userId) {
+ public boolean arePackageModesDefault(@NonNull String packageName, @UserIdInt int userId) {
synchronized (mLock) {
ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
if (packageModes == null) {
return true;
}
- SparseIntArray opModes = packageModes.get(packageMode);
+ SparseIntArray opModes = packageModes.get(packageName);
return (opModes == null || opModes.size() <= 0);
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 934542291334..9cf4082617fb 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1909,7 +1909,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
try {
pvr = verifyAndGetBypass(uid, packageName, null);
} catch (SecurityException e) {
- Slog.e(TAG, "Cannot setMode", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot setMode: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot setMode", e);
+ }
return;
}
@@ -2322,7 +2326,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
try {
pvr = verifyAndGetBypass(uid, packageName, null);
} catch (SecurityException e) {
- Slog.e(TAG, "checkOperation", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot checkOperation: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot checkOperation", e);
+ }
return AppOpsManager.opToDefaultMode(code);
}
@@ -2534,7 +2542,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
attributionTag = null;
}
} catch (SecurityException e) {
- Slog.e(TAG, "noteOperation", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot noteOperation: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot noteOperation", e);
+ }
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -3055,7 +3067,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
attributionTag = null;
}
} catch (SecurityException e) {
- Slog.e(TAG, "startOperation", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot startOperation: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot startOperation", e);
+ }
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -3231,7 +3247,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
attributionTag = null;
}
} catch (SecurityException e) {
- Slog.e(TAG, "Cannot finishOperation", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot finishOperation: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot finishOperation", e);
+ }
return;
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 41ca13f5d5f5..7c8e6df4acdc 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -49,7 +49,6 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -61,7 +60,9 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
+import com.android.server.biometrics.log.OperationContextExt;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -106,6 +107,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
private final Context mContext;
+ @NonNull private final BiometricContext mBiometricContext;
private final IStatusBarService mStatusBarService;
@VisibleForTesting final IBiometricSysuiReceiver mSysuiReceiver;
private final KeyStore mKeyStore;
@@ -148,6 +150,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
private long mAuthenticatedTimeMs;
AuthSession(@NonNull Context context,
+ @NonNull BiometricContext biometricContext,
@NonNull IStatusBarService statusBarService,
@NonNull IBiometricSysuiReceiver sysuiReceiver,
@NonNull KeyStore keystore,
@@ -166,6 +169,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
@NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties) {
Slog.d(TAG, "Creating AuthSession with: " + preAuthInfo);
mContext = context;
+ mBiometricContext = biometricContext;
mStatusBarService = statusBarService;
mSysuiReceiver = sysuiReceiver;
mKeyStore = keystore;
@@ -694,10 +698,8 @@ public final class AuthSession implements IBinder.DeathRecipient {
+ ", Latency: " + latency);
}
- final OperationContext operationContext = new OperationContext();
- operationContext.isCrypto = isCrypto();
BiometricFrameworkStatsLogger.getInstance().authenticate(
- operationContext,
+ mBiometricContext.updateContext(new OperationContextExt(), isCrypto()),
statsModality(),
BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
@@ -726,10 +728,8 @@ public final class AuthSession implements IBinder.DeathRecipient {
+ ", Latency: " + latency);
}
// Auth canceled
- final OperationContext operationContext = new OperationContext();
- operationContext.isCrypto = isCrypto();
BiometricFrameworkStatsLogger.getInstance().error(
- operationContext,
+ mBiometricContext.updateContext(new OperationContextExt(), isCrypto()),
statsModality(),
BiometricsProtoEnums.ACTION_AUTHENTICATE,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index d97195383778..c8d43e4c40c6 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1314,7 +1314,7 @@ public class BiometricService extends SystemService {
}
final boolean debugEnabled = mInjector.isDebugEnabled(getContext(), userId);
- mAuthSession = new AuthSession(getContext(), mStatusBarService,
+ mAuthSession = new AuthSession(getContext(), mBiometricContext, mStatusBarService,
createSysuiReceiver(requestId), mKeyStore, mRandom,
createClientDeathReceiver(requestId), preAuthInfo, token, requestId,
operationId, userId, createBiometricSensorReceiver(requestId), receiver,
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
index be04364dcff3..9199acb0db82 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContext.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.common.OperationContext;
+import android.view.Surface;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
@@ -38,14 +39,14 @@ public interface BiometricContext {
}
/** Update the given context with the most recent values and return it. */
- OperationContext updateContext(@NonNull OperationContext operationContext,
+ OperationContextExt updateContext(@NonNull OperationContextExt operationContext,
boolean isCryptoOperation);
/** The session id for keyguard entry, if active, or null. */
- @Nullable Integer getKeyguardEntrySessionId();
+ @Nullable BiometricContextSessionInfo getKeyguardEntrySessionInfo();
/** The session id for biometric prompt usage, if active, or null. */
- @Nullable Integer getBiometricPromptSessionId();
+ @Nullable BiometricContextSessionInfo getBiometricPromptSessionInfo();
/** If the display is in AOD. */
boolean isAod();
@@ -53,16 +54,35 @@ public interface BiometricContext {
/** If the device is awake or is becoming awake. */
boolean isAwake();
+ /** If the display is on. */
+ boolean isDisplayOn();
+
+ /** Current dock state from {@link android.content.Intent#EXTRA_DOCK_STATE}. */
+ int getDockedState();
+
+ /**
+ * Current fold state from
+ * {@link android.hardware.biometrics.IBiometricContextListener.FoldState}.
+ */
+ int getFoldState();
+
+ /** Current device display rotation. */
+ @Surface.Rotation
+ int getCurrentRotation();
+
/**
* Subscribe to context changes.
*
+ * Note that this method only notifies for properties that are visible to the HAL.
+ *
* @param context context that will be modified when changed
* @param consumer callback when the context is modified
*/
- void subscribe(@NonNull OperationContext context, @NonNull Consumer<OperationContext> consumer);
+ void subscribe(@NonNull OperationContextExt context,
+ @NonNull Consumer<OperationContext> consumer);
/** Unsubscribe from context changes. */
- void unsubscribe(@NonNull OperationContext context);
+ void unsubscribe(@NonNull OperationContextExt context);
/** Obtains an AuthSessionCoordinator. */
AuthSessionCoordinator getAuthSessionCoordinator();
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
index d456736e7e8b..b63e8e31f73f 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -19,10 +19,12 @@ package com.android.server.biometrics.log;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.RemoteException;
@@ -30,6 +32,8 @@ import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.UserHandle;
import android.util.Slog;
+import android.view.Display;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
@@ -42,7 +46,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
/**
- * A default provider for {@link BiometricContext}.
+ * A default provider for {@link BiometricContext} that aggregates device state from SysUI
+ * and packages it into an {@link android.hardware.biometrics.common.OperationContext} that can
+ * be propagated to the HAL.
*/
public final class BiometricContextProvider implements BiometricContext {
@@ -57,7 +63,8 @@ public final class BiometricContextProvider implements BiometricContext {
synchronized (BiometricContextProvider.class) {
if (sInstance == null) {
try {
- sInstance = new BiometricContextProvider(
+ sInstance = new BiometricContextProvider(context,
+ (WindowManager) context.getSystemService(Context.WINDOW_SERVICE),
new AmbientDisplayConfiguration(context),
IStatusBarService.Stub.asInterface(ServiceManager.getServiceOrThrow(
Context.STATUS_BAR_SERVICE)), null /* handler */,
@@ -71,24 +78,47 @@ public final class BiometricContextProvider implements BiometricContext {
}
@NonNull
- private final Map<OperationContext, Consumer<OperationContext>> mSubscribers =
+ private final Map<OperationContextExt, Consumer<OperationContext>> mSubscribers =
new ConcurrentHashMap<>();
@Nullable
- private final Map<Integer, InstanceId> mSession = new ConcurrentHashMap<>();
+ private final Map<Integer, BiometricContextSessionInfo> mSession = new ConcurrentHashMap<>();
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final AuthSessionCoordinator mAuthSessionCoordinator;
+ private final WindowManager mWindowManager;
+ @Nullable private final Handler mHandler;
private boolean mIsAod = false;
private boolean mIsAwake = false;
+ private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ private int mFoldState = IBiometricContextListener.FoldState.UNKNOWN;
@VisibleForTesting
- public BiometricContextProvider(
+ final BroadcastReceiver mDockStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mDockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ // no need to notify, not sent to HAL
+ }
+ };
+
+ @VisibleForTesting
+ public BiometricContextProvider(@NonNull Context context,
+ @NonNull WindowManager windowManager,
@NonNull AmbientDisplayConfiguration ambientDisplayConfiguration,
@NonNull IStatusBarService service, @Nullable Handler handler,
- AuthSessionCoordinator authSessionCoordinator) {
+ @NonNull AuthSessionCoordinator authSessionCoordinator) {
+ mWindowManager = windowManager;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAuthSessionCoordinator = authSessionCoordinator;
+ mHandler = handler;
+
+ subscribeBiometricContextListener(service);
+ subscribeDockState(context);
+ }
+
+ private void subscribeBiometricContextListener(@NonNull IStatusBarService service) {
try {
service.setBiometicContextListener(new IBiometricContextListener.Stub() {
@Override
@@ -102,12 +132,10 @@ public final class BiometricContextProvider implements BiometricContext {
}
}
- private void notifyChanged() {
- if (handler != null) {
- handler.post(() -> notifySubscribers());
- } else {
- notifySubscribers();
- }
+ @Override
+ public void onFoldChanged(int foldState) {
+ mFoldState = foldState;
+ // no need to notify, not sent to HAL
}
private boolean isAodEnabled() {
@@ -117,13 +145,13 @@ public final class BiometricContextProvider implements BiometricContext {
service.registerSessionListener(SESSION_TYPES, new ISessionListener.Stub() {
@Override
public void onSessionStarted(int sessionType, InstanceId instance) {
- mSession.put(sessionType, instance);
+ mSession.put(sessionType, new BiometricContextSessionInfo(instance));
}
@Override
public void onSessionEnded(int sessionType, InstanceId instance) {
- final InstanceId id = mSession.remove(sessionType);
- if (id != null && instance != null && id.getId() != instance.getId()) {
+ final BiometricContextSessionInfo info = mSession.remove(sessionType);
+ if (info != null && instance != null && info.getId() != instance.getId()) {
Slog.w(TAG, "session id mismatch");
}
}
@@ -133,46 +161,28 @@ public final class BiometricContextProvider implements BiometricContext {
}
}
+ private void subscribeDockState(@NonNull Context context) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DOCK_EVENT);
+ context.registerReceiver(mDockStateReceiver, filter);
+ }
+
@Override
- public OperationContext updateContext(@NonNull OperationContext operationContext,
+ public OperationContextExt updateContext(@NonNull OperationContextExt operationContext,
boolean isCryptoOperation) {
- operationContext.isAod = isAod();
- operationContext.isCrypto = isCryptoOperation;
- setFirstSessionId(operationContext);
- return operationContext;
- }
-
- private void setFirstSessionId(@NonNull OperationContext operationContext) {
- Integer sessionId = getKeyguardEntrySessionId();
- if (sessionId != null) {
- operationContext.id = sessionId;
- operationContext.reason = OperationReason.KEYGUARD;
- return;
- }
-
- sessionId = getBiometricPromptSessionId();
- if (sessionId != null) {
- operationContext.id = sessionId;
- operationContext.reason = OperationReason.BIOMETRIC_PROMPT;
- return;
- }
-
- operationContext.id = 0;
- operationContext.reason = OperationReason.UNKNOWN;
+ return operationContext.update(this);
}
@Nullable
@Override
- public Integer getKeyguardEntrySessionId() {
- final InstanceId id = mSession.get(StatusBarManager.SESSION_KEYGUARD);
- return id != null ? id.getId() : null;
+ public BiometricContextSessionInfo getKeyguardEntrySessionInfo() {
+ return mSession.get(StatusBarManager.SESSION_KEYGUARD);
}
@Nullable
@Override
- public Integer getBiometricPromptSessionId() {
- final InstanceId id = mSession.get(StatusBarManager.SESSION_BIOMETRIC_PROMPT);
- return id != null ? id.getId() : null;
+ public BiometricContextSessionInfo getBiometricPromptSessionInfo() {
+ return mSession.get(StatusBarManager.SESSION_BIOMETRIC_PROMPT);
}
@Override
@@ -186,13 +196,33 @@ public final class BiometricContextProvider implements BiometricContext {
}
@Override
- public void subscribe(@NonNull OperationContext context,
+ public boolean isDisplayOn() {
+ return mWindowManager.getDefaultDisplay().getState() == Display.STATE_ON;
+ }
+
+ @Override
+ public int getDockedState() {
+ return mDockState;
+ }
+
+ @Override
+ public int getFoldState() {
+ return mFoldState;
+ }
+
+ @Override
+ public int getCurrentRotation() {
+ return mWindowManager.getDefaultDisplay().getRotation();
+ }
+
+ @Override
+ public void subscribe(@NonNull OperationContextExt context,
@NonNull Consumer<OperationContext> consumer) {
mSubscribers.put(context, consumer);
}
@Override
- public void unsubscribe(@NonNull OperationContext context) {
+ public void unsubscribe(@NonNull OperationContextExt context) {
mSubscribers.remove(context);
}
@@ -201,10 +231,28 @@ public final class BiometricContextProvider implements BiometricContext {
return mAuthSessionCoordinator;
}
+ private void notifyChanged() {
+ if (mHandler != null) {
+ mHandler.post(this::notifySubscribers);
+ } else {
+ notifySubscribers();
+ }
+ }
+
private void notifySubscribers() {
mSubscribers.forEach((context, consumer) -> {
- context.isAod = isAod();
- consumer.accept(context);
+ consumer.accept(context.update(this).toAidlContext());
});
}
+
+ @Override
+ public String toString() {
+ return "[keyguard session: " + getKeyguardEntrySessionInfo() + ", "
+ + "bp session: " + getBiometricPromptSessionInfo() + ", "
+ + "isAod: " + isAod() + ", "
+ + "isAwake: " + isAwake() + ", "
+ + "isDisplayOn: " + isDisplayOn() + ", "
+ + "dock: " + getDockedState() + ", "
+ + "rotation: " + getCurrentRotation() + "]";
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextSessionInfo.java b/services/core/java/com/android/server/biometrics/log/BiometricContextSessionInfo.java
new file mode 100644
index 000000000000..70f589766c7c
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextSessionInfo.java
@@ -0,0 +1,59 @@
+/*
+ * 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.biometrics.log;
+
+import android.annotation.NonNull;
+
+import com.android.internal.logging.InstanceId;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** State for an authentication session {@see com.android.internal.statusbar.ISessionListener}. */
+class BiometricContextSessionInfo {
+ private final InstanceId mId;
+ private final AtomicInteger mOrder = new AtomicInteger(0);
+
+ /** Wrap a session id with the initial state. */
+ BiometricContextSessionInfo(@NonNull InstanceId id) {
+ mId = id;
+ }
+
+ /** Get the session id. */
+ public int getId() {
+ return mId.getId();
+ }
+
+ /** Gets the current order counter for the session. */
+ public int getOrder() {
+ return mOrder.get();
+ }
+
+ /**
+ * Gets the current order counter for the session and increment the counter.
+ *
+ * This should be called by the framework after processing any logged events,
+ * such as success / failure, to preserve the order each event was processed in.
+ */
+ public int getOrderAndIncrement() {
+ return mOrder.getAndIncrement();
+ }
+
+ @Override
+ public String toString() {
+ return "[sid: " + mId.getId() + "]";
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index 27a70c51f667..82444f0cc299 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -17,9 +17,10 @@
package com.android.server.biometrics.log;
import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.common.OperationReason;
import android.util.Slog;
+import android.view.Surface;
import com.android.internal.util.FrameworkStatsLog;
@@ -41,32 +42,38 @@ public class BiometricFrameworkStatsLogger {
}
/** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */
- public void acquired(OperationContext operationContext,
+ public void acquired(OperationContextExt operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug,
int acquiredInfo, int vendorCode, int targetUserId) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
statsModality,
targetUserId,
- operationContext.isCrypto,
+ operationContext.isCrypto(),
statsAction,
statsClient,
acquiredInfo,
vendorCode,
isDebug,
-1 /* sensorId */,
- operationContext.id,
- sessionType(operationContext.reason),
- operationContext.isAod);
+ operationContext.getId(),
+ sessionType(operationContext.getReason()),
+ operationContext.isAod(),
+ operationContext.isDisplayOn(),
+ operationContext.getDockState(),
+ orientationType(operationContext.getOrientation()),
+ foldType(operationContext.getFoldState()),
+ operationContext.getOrderAndIncrement(),
+ BiometricsProtoEnums.WAKE_REASON_UNKNOWN);
}
/** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
- public void authenticate(OperationContext operationContext,
+ public void authenticate(OperationContextExt operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
int authState, boolean requireConfirmation, int targetUserId, float ambientLightLux) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
statsModality,
targetUserId,
- operationContext.isCrypto,
+ operationContext.isCrypto(),
statsClient,
requireConfirmation,
authState,
@@ -74,13 +81,19 @@ public class BiometricFrameworkStatsLogger {
isDebug,
-1 /* sensorId */,
ambientLightLux,
- operationContext.id,
- sessionType(operationContext.reason),
- operationContext.isAod);
+ operationContext.getId(),
+ sessionType(operationContext.getReason()),
+ operationContext.isAod(),
+ operationContext.isDisplayOn(),
+ operationContext.getDockState(),
+ orientationType(operationContext.getOrientation()),
+ foldType(operationContext.getFoldState()),
+ operationContext.getOrderAndIncrement(),
+ BiometricsProtoEnums.WAKE_REASON_UNKNOWN);
}
/** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
- public void authenticate(OperationContext operationContext,
+ public void authenticate(OperationContextExt operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
int authState, boolean requireConfirmation, int targetUserId, ALSProbe alsProbe) {
alsProbe.awaitNextLux((ambientLightLux) -> {
@@ -102,13 +115,13 @@ public class BiometricFrameworkStatsLogger {
}
/** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
- public void error(OperationContext operationContext,
+ public void error(OperationContextExt operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
int error, int vendorCode, int targetUserId) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
statsModality,
targetUserId,
- operationContext.isCrypto,
+ operationContext.isCrypto(),
statsAction,
statsClient,
error,
@@ -116,9 +129,15 @@ public class BiometricFrameworkStatsLogger {
isDebug,
sanitizeLatency(latency),
-1 /* sensorId */,
- operationContext.id,
- sessionType(operationContext.reason),
- operationContext.isAod);
+ operationContext.getId(),
+ sessionType(operationContext.getReason()),
+ operationContext.isAod(),
+ operationContext.isDisplayOn(),
+ operationContext.getDockState(),
+ orientationType(operationContext.getOrientation()),
+ foldType(operationContext.getFoldState()),
+ operationContext.getOrderAndIncrement(),
+ BiometricsProtoEnums.WAKE_REASON_UNKNOWN);
}
/** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
@@ -154,4 +173,30 @@ public class BiometricFrameworkStatsLogger {
}
return BiometricsProtoEnums.SESSION_TYPE_UNKNOWN;
}
+
+ private static int orientationType(@Surface.Rotation int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ return BiometricsProtoEnums.ORIENTATION_0;
+ case Surface.ROTATION_90:
+ return BiometricsProtoEnums.ORIENTATION_90;
+ case Surface.ROTATION_180:
+ return BiometricsProtoEnums.ORIENTATION_180;
+ case Surface.ROTATION_270:
+ return BiometricsProtoEnums.ORIENTATION_270;
+ }
+ return BiometricsProtoEnums.ORIENTATION_UNKNOWN;
+ }
+
+ private static int foldType(int foldType) {
+ switch (foldType) {
+ case IBiometricContextListener.FoldState.FULLY_CLOSED:
+ return BiometricsProtoEnums.FOLD_CLOSED;
+ case IBiometricContextListener.FoldState.FULLY_OPENED:
+ return BiometricsProtoEnums.FOLD_OPEN;
+ case IBiometricContextListener.FoldState.HALF_OPENED:
+ return BiometricsProtoEnums.FOLD_HALF_OPEN;
+ }
+ return BiometricsProtoEnums.FOLD_UNKNOWN;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index 55fe854e1404..c76a2e38aabc 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.util.Slog;
@@ -118,7 +117,7 @@ public class BiometricLogger {
}
/** Log an acquisition event. */
- public void logOnAcquired(Context context, OperationContext operationContext,
+ public void logOnAcquired(Context context, OperationContextExt operationContext,
int acquiredInfo, int vendorCode, int targetUserId) {
if (!mShouldLogMetrics) {
return;
@@ -139,7 +138,7 @@ public class BiometricLogger {
if (DEBUG) {
Slog.v(TAG, "Acquired! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + operationContext.isCrypto
+ + ", IsCrypto: " + operationContext.isCrypto()
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", AcquiredInfo: " + acquiredInfo
@@ -150,13 +149,14 @@ public class BiometricLogger {
return;
}
- mSink.acquired(operationContext, mStatsModality, mStatsAction, mStatsClient,
+ mSink.acquired(operationContext,
+ mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
acquiredInfo, vendorCode, targetUserId);
}
/** Log an error during an operation. */
- public void logOnError(Context context, OperationContext operationContext,
+ public void logOnError(Context context, OperationContextExt operationContext,
int error, int vendorCode, int targetUserId) {
if (!mShouldLogMetrics) {
return;
@@ -168,7 +168,7 @@ public class BiometricLogger {
if (DEBUG) {
Slog.v(TAG, "Error! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + operationContext.isCrypto
+ + ", IsCrypto: " + operationContext.isCrypto()
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", Error: " + error
@@ -182,15 +182,16 @@ public class BiometricLogger {
return;
}
- mSink.error(operationContext, mStatsModality, mStatsAction, mStatsClient,
+ mSink.error(operationContext,
+ mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId), latency,
error, vendorCode, targetUserId);
}
/** Log authentication attempt. */
- public void logOnAuthenticated(Context context, OperationContext operationContext,
- boolean authenticated, boolean requireConfirmation,
- int targetUserId, boolean isBiometricPrompt) {
+ public void logOnAuthenticated(Context context, OperationContextExt operationContext,
+ boolean authenticated, boolean requireConfirmation, int targetUserId,
+ boolean isBiometricPrompt) {
if (!mShouldLogMetrics) {
return;
}
@@ -215,7 +216,7 @@ public class BiometricLogger {
if (DEBUG) {
Slog.v(TAG, "Authenticated! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + operationContext.isCrypto
+ + ", IsCrypto: " + operationContext.isCrypto()
+ ", Client: " + mStatsClient
+ ", RequireConfirmation: " + requireConfirmation
+ ", State: " + authState
@@ -229,7 +230,8 @@ public class BiometricLogger {
return;
}
- mSink.authenticate(operationContext, mStatsModality, mStatsAction, mStatsClient,
+ mSink.authenticate(operationContext,
+ mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
latency, authState, requireConfirmation, targetUserId, mALSProbe);
}
diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
new file mode 100644
index 000000000000..42be95b7377a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
@@ -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.server.biometrics.log;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.hardware.biometrics.IBiometricContextListener;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.OperationReason;
+import android.view.Surface;
+
+/**
+ * Wrapper around {@link OperationContext} to include properties that are not
+ * shared with the HAL.
+ *
+ * When useful, these properties should move to the wrapped object for use by HAL in
+ * future releases.
+ */
+public class OperationContextExt {
+
+ @NonNull private final OperationContext mAidlContext;
+ @Nullable private BiometricContextSessionInfo mSessionInfo;
+ private boolean mIsDisplayOn = false;
+ private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ @Surface.Rotation private int mOrientation = Surface.ROTATION_0;
+ private int mFoldState = IBiometricContextListener.FoldState.UNKNOWN;
+
+ /** Create a new empty context. */
+ public OperationContextExt() {
+ this(new OperationContext());
+ }
+
+ /** Create a wrapped context. */
+ public OperationContextExt(@NonNull OperationContext context) {
+ mAidlContext = context;
+ }
+
+ /** Gets the subset of the context that can be shared with the HAL. */
+ @NonNull
+ public OperationContext toAidlContext() {
+ return mAidlContext;
+ }
+
+ /** {@link OperationContext#id}. */
+ public int getId() {
+ return mAidlContext.id;
+ }
+
+ /** Gets the current order counter for the session and increment the counter. */
+ public int getOrderAndIncrement() {
+ final BiometricContextSessionInfo info = mSessionInfo;
+ return info != null ? info.getOrderAndIncrement() : -1;
+ }
+
+ /** {@link OperationContext#reason}. */
+ public byte getReason() {
+ return mAidlContext.reason;
+ }
+
+ /** If the screen is currently on. */
+ public boolean isDisplayOn() {
+ return mIsDisplayOn;
+ }
+
+ /** {@link OperationContext#isAod}. */
+ public boolean isAod() {
+ return mAidlContext.isAod;
+ }
+
+ /** {@link OperationContext#isCrypto}. */
+ public boolean isCrypto() {
+ return mAidlContext.isCrypto;
+ }
+
+ /** The dock state when this event occurred {@see Intent.EXTRA_DOCK_STATE_UNDOCKED}. */
+ public int getDockState() {
+ return mDockState;
+ }
+
+ /** The fold state of the device when this event occurred. */
+ public int getFoldState() {
+ return mFoldState;
+ }
+
+ /** The orientation of the device when this event occurred. */
+ @Surface.Rotation
+ public int getOrientation() {
+ return mOrientation;
+ }
+
+ /** Update this object with the latest values from the given context. */
+ OperationContextExt update(@NonNull BiometricContext biometricContext) {
+ mAidlContext.isAod = biometricContext.isAod();
+ setFirstSessionId(biometricContext);
+
+ mIsDisplayOn = biometricContext.isDisplayOn();
+ mDockState = biometricContext.getDockedState();
+ mFoldState = biometricContext.getFoldState();
+ mOrientation = biometricContext.getCurrentRotation();
+
+ return this;
+ }
+
+ private void setFirstSessionId(@NonNull BiometricContext biometricContext) {
+ mSessionInfo = biometricContext.getKeyguardEntrySessionInfo();
+ if (mSessionInfo != null) {
+ mAidlContext.id = mSessionInfo.getId();
+ mAidlContext.reason = OperationReason.KEYGUARD;
+ return;
+ }
+
+ mSessionInfo = biometricContext.getBiometricPromptSessionInfo();
+ if (mSessionInfo != null) {
+ mAidlContext.id = mSessionInfo.getId();
+ mAidlContext.reason = OperationReason.BIOMETRIC_PROMPT;
+ return;
+ }
+
+ // no session
+ mAidlContext.id = 0;
+ mAidlContext.reason = OperationReason.UNKNOWN;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index a6e89115400d..d0a4807d937e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -19,11 +19,11 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.common.OperationContext;
import android.os.IBinder;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.OperationContextExt;
import java.util.function.Supplier;
@@ -37,7 +37,7 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
protected final Supplier<T> mLazyDaemon;
@NonNull
- private final OperationContext mOperationContext = new OperationContext();
+ private final OperationContextExt mOperationContext = new OperationContextExt();
/**
* @param context system_server context
@@ -85,7 +85,7 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
unsubscribeBiometricContext();
}
- protected OperationContext getOperationContext() {
+ protected OperationContextExt getOperationContext() {
return getBiometricContext().updateContext(mOperationContext, isCryptoOperation());
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index b1cb257b2443..d8b825d2c0e5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -164,7 +164,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
if (session.hasContextMethods()) {
return session.getSession().authenticateWithContext(
- mOperationId, getOperationContext());
+ mOperationId, getOperationContext().toAidlContext());
} else {
return session.getSession().authenticate(mOperationId);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index ded18100e458..506b2bc8d9db 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -115,7 +115,8 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- return session.getSession().detectInteractionWithContext(getOperationContext());
+ return session.getSession().detectInteractionWithContext(
+ getOperationContext().toAidlContext());
} else {
return session.getSession().detectInteraction();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 5d62cde01a6f..792b52e7d658 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -199,7 +199,8 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
if (session.hasContextMethods()) {
return session.getSession().enrollWithContext(
- hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, getOperationContext());
+ hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle,
+ getOperationContext().toAidlContext());
} else {
return session.getSession().enroll(hat, EnrollmentType.DEFAULT, features,
mHwPreviewHandle);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 11f45176dc45..9669950e236e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -29,7 +29,6 @@ import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -47,6 +46,7 @@ import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
@@ -333,7 +333,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
private ICancellationSignal doAuthenticate() throws RemoteException {
final AidlSession session = getFreshDaemon();
- final OperationContext opContext = getOperationContext();
+ final OperationContextExt opContext = getOperationContext();
getBiometricContext().subscribe(opContext, ctx -> {
if (session.hasContextMethods()) {
try {
@@ -356,7 +356,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
}
if (session.hasContextMethods()) {
- return session.getSession().authenticateWithContext(mOperationId, opContext);
+ return session.getSession().authenticateWithContext(
+ mOperationId, opContext.toAidlContext());
} else {
return session.getSession().authenticate(mOperationId);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 52822347e96f..f6911ea29837 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -102,7 +102,8 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- return session.getSession().detectInteractionWithContext(getOperationContext());
+ return session.getSession().detectInteractionWithContext(
+ getOperationContext().toAidlContext());
} else {
return session.getSession().detectInteraction();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index fa54983b31dc..2ac8b433b3af 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -24,7 +24,6 @@ import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
@@ -42,6 +41,7 @@ import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
@@ -185,9 +185,9 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
if (session.hasContextMethods()) {
- final OperationContext opContext = getOperationContext();
+ final OperationContextExt opContext = getOperationContext();
final ICancellationSignal cancel = session.getSession().enrollWithContext(
- hat, opContext);
+ hat, opContext.toAidlContext());
getBiometricContext().subscribe(opContext, ctx -> {
try {
session.getSession().onContextChanged(ctx);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 34c6265a5368..73b1288d00d2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -418,7 +418,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
}
@Override
- public void onTrustChanged(boolean enabled, int userId, int flags,
+ public void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags,
List<String> trustGrantedMessages) {
mUserHasTrust.put(userId, enabled);
}
diff --git a/services/core/java/com/android/server/cpu/CpuInfoReader.java b/services/core/java/com/android/server/cpu/CpuInfoReader.java
index b13d7533f29b..ca97a9847b39 100644
--- a/services/core/java/com/android/server/cpu/CpuInfoReader.java
+++ b/services/core/java/com/android/server/cpu/CpuInfoReader.java
@@ -16,11 +16,15 @@
package com.android.server.cpu;
+import static com.android.server.cpu.CpuMonitorService.DEBUG;
+import static com.android.server.cpu.CpuMonitorService.TAG;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.system.Os;
import android.system.OsConstants;
-import android.util.ArrayMap;
+import android.util.IntArray;
+import android.util.LongSparseLongArray;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -31,8 +35,7 @@ import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
@@ -40,7 +43,6 @@ import java.util.regex.Pattern;
/** Reader to read CPU information from proc and sys fs files exposed by the Kernel. */
public final class CpuInfoReader {
- static final String TAG = CpuInfoReader.class.getSimpleName();
static final int FLAG_CPUSET_CATEGORY_TOP_APP = 1 << 0;
static final int FLAG_CPUSET_CATEGORY_BACKGROUND = 1 << 1;
@@ -67,7 +69,7 @@ public final class CpuInfoReader {
+ "(?<guestNiceClockTicks>[0-9]+)");
private static final Pattern TIME_IN_STATE_PATTERN =
Pattern.compile("(?<freqKHz>[0-9]+)\\s(?<time>[0-9]+)");
- private static final long MILLIS_PER_JIFFY = 1000L / Os.sysconf(OsConstants._SC_CLK_TCK);
+ private static final long MILLIS_PER_CLOCK_TICK = 1000L / Os.sysconf(OsConstants._SC_CLK_TCK);
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FLAG_CPUSET_CATEGORY_"}, flag = true, value = {
@@ -76,14 +78,15 @@ public final class CpuInfoReader {
})
private @interface CpusetCategory{}
+ // TODO(b/242722241): Protect updatable variables with a local lock.
private final File mCpusetDir;
private final SparseIntArray mCpusetCategoriesByCpus = new SparseIntArray();
- private final SparseArray<Long> mMaxCpuFrequenciesByCpus = new SparseArray<>();
- private final ArrayMap<String, ArrayMap<Long, Long>> mTimeInStateByPolicy = new ArrayMap<>();
+ private final SparseArray<File> mCpuFreqPolicyDirsById = new SparseArray<>();
+ private final SparseArray<StaticPolicyInfo> mStaticPolicyInfoById = new SparseArray<>();
+ private final SparseArray<LongSparseLongArray> mTimeInStateByPolicyId = new SparseArray<>();
private File mCpuFreqDir;
private File mProcStatFile;
- private File[] mCpuFreqPolicyDirs;
private SparseArray<CpuUsageStats> mCumulativeCpuUsageStats = new SparseArray<>();
private boolean mIsEnabled;
private boolean mHasTimeInStateFile;
@@ -99,88 +102,206 @@ public final class CpuInfoReader {
mProcStatFile = procStatFile;
}
- /** Inits CpuInfoReader and returns a boolean to indicate whether the reader is enabled. */
+ /**
+ * Initializes CpuInfoReader and returns a boolean to indicate whether the reader is enabled.
+ */
public boolean init() {
- mCpuFreqPolicyDirs = mCpuFreqDir.listFiles(
+ if (mCpuFreqPolicyDirsById.size() > 0) {
+ Slogf.w(TAG, "Ignoring duplicate CpuInfoReader init request");
+ return mIsEnabled;
+ }
+ File[] policyDirs = mCpuFreqDir.listFiles(
file -> file.isDirectory() && file.getName().startsWith(POLICY_DIR_PREFIX));
- if (mCpuFreqPolicyDirs == null || mCpuFreqPolicyDirs.length == 0) {
+ if (policyDirs == null || policyDirs.length == 0) {
Slogf.w(TAG, "Missing CPU frequency policy directories at %s",
mCpuFreqDir.getAbsolutePath());
return false;
}
+ populateCpuFreqPolicyDirsById(policyDirs);
+ if (mCpuFreqPolicyDirsById.size() == 0) {
+ Slogf.e(TAG, "Failed to parse CPU frequency policy directory paths: %s",
+ Arrays.toString(policyDirs));
+ return false;
+ }
+ readStaticPolicyInfo();
+ if (mStaticPolicyInfoById.size() == 0) {
+ Slogf.e(TAG, "Failed to read static CPU frequency policy info from policy dirs: %s",
+ Arrays.toString(policyDirs));
+ return false;
+ }
if (!mProcStatFile.exists()) {
Slogf.e(TAG, "Missing proc stat file at %s", mProcStatFile.getAbsolutePath());
return false;
}
readCpusetCategories();
if (mCpusetCategoriesByCpus.size() == 0) {
- Slogf.e(TAG, "Failed to read cpuset information read from %s",
- mCpusetDir.getAbsolutePath());
+ Slogf.e(TAG, "Failed to read cpuset information from %s", mCpusetDir.getAbsolutePath());
return false;
}
- readMaxCpuFrequencies();
- if (mMaxCpuFrequenciesByCpus.size() == 0) {
- Slogf.e(TAG, "Failed to read max CPU frequencies from policy directories at %s",
- mCpuFreqDir.getAbsolutePath());
- return false;
+ // Certain CPU performance scaling drivers, such as intel_pstate, perform their own CPU
+ // frequency transitions and do not supply this information to the Kernel's cpufreq node.
+ // Thus, the `time_in_state` file won't be available on devices running such scaling
+ // drivers. Check the presence of this file only once during init and do not throw error
+ // when this file is missing. The implementation must accommodate such use cases.
+ for (int i = 0; i < mCpuFreqPolicyDirsById.size() && !mHasTimeInStateFile; ++i) {
+ // If all the CPU cores on a policy are offline, this file might be missing for the
+ // policy. Make sure this file is not available on all policies before marking it as
+ // missing.
+ mHasTimeInStateFile |= new File(mCpuFreqPolicyDirsById.valueAt(i), TIME_IN_STATE_FILE)
+ .exists();
+ }
+ if (!mHasTimeInStateFile) {
+ Slogf.e(TAG, "Time in state file not available for any cpufreq policy");
}
-
- // The Kernel must be configured to generate the `time_in_state` file. If the Kernel is not
- // configured to generate this file, this file won't be available for the entire system
- // uptime. Thus, check for the presence of this file only during init.
- mHasTimeInStateFile = new File(mCpuFreqPolicyDirs[0], TIME_IN_STATE_FILE).exists();
mIsEnabled = true;
return true;
}
- /** Reads CPU information from proc and sys fs files exposed by the Kernel. */
- public List<CpuInfo> readCpuInfos() {
+ /**
+ * Reads CPU information from proc and sys fs files exposed by the Kernel.
+ *
+ * @return SparseArray keyed by CPU core ID; {@code null} on error or when disabled.
+ */
+ @Nullable
+ public SparseArray<CpuInfo> readCpuInfos() {
if (!mIsEnabled) {
- return Collections.emptyList();
+ return null;
}
- SparseArray<CpuUsageStats> latestCpuUsageStats = readLatestCpuUsageStats();
- if (latestCpuUsageStats == null) {
+ SparseArray<CpuUsageStats> cpuUsageStatsByCpus = readLatestCpuUsageStats();
+ if (cpuUsageStatsByCpus == null || cpuUsageStatsByCpus.size() == 0) {
Slogf.e(TAG, "Failed to read latest CPU usage stats");
- return Collections.emptyList();
- }
- SparseArray<Long> cpuFrequenciesByCpus = readCurrentCpuFrequencies();
- List<CpuInfo> cpuInfos = new ArrayList<>();
- for (int i = 0; i < cpuFrequenciesByCpus.size(); i++) {
- int cpu = cpuFrequenciesByCpus.keyAt(i);
- long curFrequency = cpuFrequenciesByCpus.valueAt(i);
- if (!mMaxCpuFrequenciesByCpus.contains(cpu) || !latestCpuUsageStats.contains(cpu)) {
- Slogf.w(TAG, "Missing max CPU frequency or CPU usage stats for CPU core %d", cpu);
+ return null;
+ }
+ SparseArray<DynamicPolicyInfo> dynamicPolicyInfoById = readDynamicPolicyInfo();
+ if (dynamicPolicyInfoById.size() == 0) {
+ Slogf.e(TAG, "Failed to read dynamic policy infos");
+ return null;
+ }
+ SparseArray<CpuInfo> cpuInfoByCpus = new SparseArray<>();
+ for (int i = 0; i < mStaticPolicyInfoById.size(); i++) {
+ int policyId = mStaticPolicyInfoById.keyAt(i);
+ StaticPolicyInfo staticPolicyInfo = mStaticPolicyInfoById.valueAt(i);
+ DynamicPolicyInfo dynamicPolicyInfo = dynamicPolicyInfoById.get(policyId);
+ if (dynamicPolicyInfo == null) {
+ Slogf.w(TAG, "Missing dynamic policy info for policy ID %d", policyId);
continue;
}
- int cpuCategories = mCpusetCategoriesByCpus.get(cpu, -1);
- if (cpuCategories < 0) {
- Slogf.w(TAG, "Missing cpuset information for CPU core %d", cpu);
+ long curFreqKHz = CpuInfo.MISSING_FREQUENCY;
+ long maxFreqKHz = CpuInfo.MISSING_FREQUENCY;
+ if (dynamicPolicyInfo.curCpuFreqPair.cpuFreqKHz != CpuInfo.MISSING_FREQUENCY
+ && staticPolicyInfo.maxCpuFreqPair.cpuFreqKHz != CpuInfo.MISSING_FREQUENCY) {
+ curFreqKHz = dynamicPolicyInfo.curCpuFreqPair.cpuFreqKHz;
+ maxFreqKHz = staticPolicyInfo.maxCpuFreqPair.cpuFreqKHz;
+ } else if (dynamicPolicyInfo.curCpuFreqPair.scalingFreqKHz != CpuInfo.MISSING_FREQUENCY
+ && staticPolicyInfo.maxCpuFreqPair.scalingFreqKHz
+ != CpuInfo.MISSING_FREQUENCY) {
+ curFreqKHz = dynamicPolicyInfo.curCpuFreqPair.scalingFreqKHz;
+ maxFreqKHz = staticPolicyInfo.maxCpuFreqPair.scalingFreqKHz;
+ } else {
+ Slogf.w(TAG, "Current and maximum CPU frequency information mismatch/missing for"
+ + " policy ID %d", policyId);
continue;
}
- cpuInfos.add(new CpuInfo(cpu, cpuCategories, curFrequency,
- mMaxCpuFrequenciesByCpus.get(cpu), latestCpuUsageStats.get(cpu)));
+ for (int coreIdx = 0; coreIdx < staticPolicyInfo.relatedCpuCores.size(); coreIdx++) {
+ int relatedCpuCore = staticPolicyInfo.relatedCpuCores.get(coreIdx);
+ CpuInfo prevCpuInfo = cpuInfoByCpus.get(relatedCpuCore);
+ if (prevCpuInfo != null) {
+ Slogf.wtf(TAG, "CPU info already available for the CPU core %d",
+ relatedCpuCore);
+ if (prevCpuInfo.isOnline) {
+ continue;
+ }
+ }
+ int cpusetCategories = mCpusetCategoriesByCpus.get(relatedCpuCore, -1);
+ if (cpusetCategories < 0) {
+ Slogf.w(TAG, "Missing cpuset information for the CPU core %d",
+ relatedCpuCore);
+ continue;
+ }
+ CpuUsageStats usageStats = cpuUsageStatsByCpus.get(relatedCpuCore);
+ if (dynamicPolicyInfo.affectedCpuCores.indexOf(relatedCpuCore) < 0) {
+ cpuInfoByCpus.append(relatedCpuCore, new CpuInfo(relatedCpuCore,
+ cpusetCategories, /* isOnline= */false, CpuInfo.MISSING_FREQUENCY,
+ maxFreqKHz, CpuInfo.MISSING_FREQUENCY, usageStats));
+ continue;
+ }
+ // If a CPU core is online, it must have the usage stats. When the usage stats is
+ // missing, drop the core's CPU info.
+ if (usageStats == null) {
+ Slogf.w(TAG, "Missing CPU usage information for online CPU core %d",
+ relatedCpuCore);
+ continue;
+ }
+ CpuInfo cpuInfo = new CpuInfo(relatedCpuCore, cpusetCategories, /* isOnline= */true,
+ curFreqKHz, maxFreqKHz, dynamicPolicyInfo.avgTimeInStateCpuFreqKHz,
+ usageStats);
+ cpuInfoByCpus.append(relatedCpuCore, cpuInfo);
+ if (DEBUG) {
+ Slogf.d(TAG, "Added %s for CPU core %d", cpuInfo, relatedCpuCore);
+ }
+ }
}
- return cpuInfos;
+ return cpuInfoByCpus;
}
+ /**
+ * Sets the CPU frequency for testing.
+ *
+ * <p>Return {@code true} on success. Otherwise, returns {@code false}.
+ */
@VisibleForTesting
- void setCpuFreqDir(File cpuFreqDir) {
+ boolean setCpuFreqDir(File cpuFreqDir) {
File[] cpuFreqPolicyDirs = cpuFreqDir.listFiles(
file -> file.isDirectory() && file.getName().startsWith(POLICY_DIR_PREFIX));
- if (mCpuFreqPolicyDirs == null || mCpuFreqPolicyDirs.length == 0) {
+ if (cpuFreqPolicyDirs == null || cpuFreqPolicyDirs.length == 0) {
Slogf.w(TAG, "Failed to set CPU frequency directory. Missing policy directories at %s",
- mCpuFreqDir.getAbsolutePath());
- return;
+ cpuFreqDir.getAbsolutePath());
+ return false;
+ }
+ populateCpuFreqPolicyDirsById(cpuFreqPolicyDirs);
+ int numCpuFreqPolicyDirs = mCpuFreqPolicyDirsById.size();
+ int numStaticPolicyInfos = mStaticPolicyInfoById.size();
+ if (numCpuFreqPolicyDirs == 0 || numCpuFreqPolicyDirs != numStaticPolicyInfos) {
+ Slogf.e(TAG, "Failed to set CPU frequency directory to %s. Total CPU frequency "
+ + "policies (%d) under new path is either 0 or not equal to initial "
+ + "total CPU frequency policies. Clearing CPU frequency policy "
+ + "directories", cpuFreqDir.getAbsolutePath(), numCpuFreqPolicyDirs,
+ numStaticPolicyInfos);
+ mCpuFreqPolicyDirsById.clear();
+ return false;
}
mCpuFreqDir = cpuFreqDir;
- mCpuFreqPolicyDirs = cpuFreqPolicyDirs;
- Slogf.i(TAG, "Set CPU frequency directory to %s", cpuFreqDir.getAbsolutePath());
+ return true;
}
+ /**
+ * Sets the proc stat file for testing.
+ *
+ * <p>Return true on success. Otherwise, returns false.
+ */
@VisibleForTesting
- void setProcStatFile(File procStatFile) {
+ boolean setProcStatFile(File procStatFile) {
+ if (!procStatFile.exists()) {
+ Slogf.e(TAG, "Missing proc stat file at %s", procStatFile.getAbsolutePath());
+ return false;
+ }
mProcStatFile = procStatFile;
- Slogf.i(TAG, "Set proc stat file to %s", procStatFile.getAbsolutePath());
+ return true;
+ }
+
+ private void populateCpuFreqPolicyDirsById(File[] policyDirs) {
+ mCpuFreqPolicyDirsById.clear();
+ for (int i = 0; i < policyDirs.length; i++) {
+ File policyDir = policyDirs[i];
+ String policyIdStr = policyDir.getName().substring(POLICY_DIR_PREFIX.length());
+ if (policyIdStr.isEmpty()) {
+ continue;
+ }
+ mCpuFreqPolicyDirsById.append(Integer.parseInt(policyIdStr), policyDir);
+ if (DEBUG) {
+ Slogf.d(TAG, "Cached policy directory %s for policy id %s", policyDir, policyIdStr);
+ }
+ }
}
private void readCpusetCategories() {
@@ -200,11 +321,13 @@ public final class CpuInfoReader {
cpusetCategory = FLAG_CPUSET_CATEGORY_BACKGROUND;
break;
default:
+ // Ignore other cpuset categories because the implementation doesn't support
+ // monitoring CPU availability for other cpusets.
continue;
}
File cpuCoresFile = new File(dir.getPath(), CPUS_FILE);
- List<Integer> cpuCores = readCpuCores(cpuCoresFile);
- if (cpuCores.isEmpty()) {
+ IntArray cpuCores = readCpuCores(cpuCoresFile);
+ if (cpuCores == null || cpuCores.size() == 0) {
Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath());
continue;
}
@@ -212,80 +335,104 @@ public final class CpuInfoReader {
int categories = mCpusetCategoriesByCpus.get(cpuCores.get(j));
categories |= cpusetCategory;
mCpusetCategoriesByCpus.append(cpuCores.get(j), categories);
+ if (DEBUG) {
+ Slogf.d(TAG, "Mapping CPU core id %d with cpuset categories [%s]",
+ cpuCores.get(j), toCpusetCategoriesStr(categories));
+ }
}
}
}
- private void readMaxCpuFrequencies() {
- for (int i = 0; i < mCpuFreqPolicyDirs.length; i++) {
- File policyDir = mCpuFreqPolicyDirs[i];
- long maxCpuFreqKHz = readMaxCpuFrequency(policyDir);
- if (maxCpuFreqKHz == 0) {
- Slogf.w(TAG, "Invalid max CPU frequency read from %s", policyDir.getAbsolutePath());
+ private void readStaticPolicyInfo() {
+ for (int i = 0; i < mCpuFreqPolicyDirsById.size(); i++) {
+ int policyId = mCpuFreqPolicyDirsById.keyAt(i);
+ File policyDir = mCpuFreqPolicyDirsById.valueAt(i);
+ FrequencyPair maxCpuFreqPair = readMaxCpuFrequency(policyDir);
+ if (maxCpuFreqPair.isEmpty()) {
+ Slogf.w(TAG, "Missing max CPU frequency information at %s",
+ policyDir.getAbsolutePath());
continue;
}
File cpuCoresFile = new File(policyDir, RELATED_CPUS_FILE);
- List<Integer> cpuCores = readCpuCores(cpuCoresFile);
- if (cpuCores.isEmpty()) {
- Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath());
+ IntArray relatedCpuCores = readCpuCores(cpuCoresFile);
+ if (relatedCpuCores == null || relatedCpuCores.size() == 0) {
+ Slogf.e(TAG, "Failed to read related CPU cores from %s",
+ cpuCoresFile.getAbsolutePath());
continue;
}
- for (int j = 0; j < cpuCores.size(); j++) {
- mMaxCpuFrequenciesByCpus.append(cpuCores.get(j), maxCpuFreqKHz);
+ StaticPolicyInfo staticPolicyInfo = new StaticPolicyInfo(maxCpuFreqPair,
+ relatedCpuCores);
+ mStaticPolicyInfoById.append(policyId, staticPolicyInfo);
+ if (DEBUG) {
+ Slogf.d(TAG, "Added static policy info %s for policy id %d", staticPolicyInfo,
+ policyId);
}
}
}
- private long readMaxCpuFrequency(File policyDir) {
- long curCpuFreqKHz = readCpuFreqKHz(new File(policyDir, MAX_CPUFREQ_FILE));
- return curCpuFreqKHz > 0 ? curCpuFreqKHz
- : readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE));
+ private FrequencyPair readMaxCpuFrequency(File policyDir) {
+ return new FrequencyPair(readCpuFreqKHz(new File(policyDir, MAX_CPUFREQ_FILE)),
+ readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE)));
}
- private SparseArray<Long> readCurrentCpuFrequencies() {
- SparseArray<Long> curCpuFrequenciesByCpus = new SparseArray<>();
- for (int i = 0; i < mCpuFreqPolicyDirs.length; i++) {
- File policyDir = mCpuFreqPolicyDirs[i];
- long curCpuFreqKHz = readCurrentCpuFrequency(policyDir);
- if (curCpuFreqKHz == 0) {
+ private SparseArray<DynamicPolicyInfo> readDynamicPolicyInfo() {
+ SparseArray<DynamicPolicyInfo> dynamicPolicyInfoById = new SparseArray<>();
+ for (int i = 0; i < mCpuFreqPolicyDirsById.size(); i++) {
+ int policyId = mCpuFreqPolicyDirsById.keyAt(i);
+ File policyDir = mCpuFreqPolicyDirsById.valueAt(i);
+ FrequencyPair curCpuFreqPair = readCurrentCpuFrequency(policyDir);
+ if (curCpuFreqPair.isEmpty()) {
Slogf.w(TAG, "Missing current frequency information at %s",
policyDir.getAbsolutePath());
continue;
}
+ long avgTimeInStateCpuFreqKHz = readAvgTimeInStateCpuFrequency(policyId, policyDir);
File cpuCoresFile = new File(policyDir, AFFECTED_CPUS_FILE);
- List<Integer> cpuCores = readCpuCores(cpuCoresFile);
- if (cpuCores.isEmpty()) {
+ IntArray affectedCpuCores = readCpuCores(cpuCoresFile);
+ if (affectedCpuCores == null || affectedCpuCores.size() == 0) {
Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath());
continue;
}
- for (int j = 0; j < cpuCores.size(); j++) {
- curCpuFrequenciesByCpus.append(cpuCores.get(j), curCpuFreqKHz);
+ DynamicPolicyInfo dynamicPolicyInfo = new DynamicPolicyInfo(curCpuFreqPair,
+ avgTimeInStateCpuFreqKHz, affectedCpuCores);
+ dynamicPolicyInfoById.append(policyId, dynamicPolicyInfo);
+ if (DEBUG) {
+ Slogf.d(TAG, "Read dynamic policy info %s for policy id %d", dynamicPolicyInfo,
+ policyId);
}
}
- return curCpuFrequenciesByCpus;
+ return dynamicPolicyInfoById;
+ }
+
+ private FrequencyPair readCurrentCpuFrequency(File policyDir) {
+ return new FrequencyPair(readCpuFreqKHz(new File(policyDir, CUR_CPUFREQ_FILE)),
+ readCpuFreqKHz(new File(policyDir, CUR_SCALING_FREQ_FILE)));
}
- private long readCurrentCpuFrequency(File policyDir) {
- ArrayMap<Long, Long> latestTimeInState = readTimeInState(policyDir);
- if (latestTimeInState == null) {
- long curCpuFreqKHz = readCpuFreqKHz(new File(policyDir, CUR_CPUFREQ_FILE));
- return curCpuFreqKHz > 0 ? curCpuFreqKHz :
- readCpuFreqKHz(new File(policyDir, CUR_SCALING_FREQ_FILE));
- }
- String policyDirName = policyDir.getName();
- if (mTimeInStateByPolicy.containsKey(policyDirName)) {
- ArrayMap<Long, Long> prevTimeInState = mTimeInStateByPolicy.get(policyDirName);
- ArrayMap<Long, Long> deltaTimeInState =
- calculateDeltaTimeInState(prevTimeInState, latestTimeInState);
- mTimeInStateByPolicy.put(policyDirName, latestTimeInState);
- return calculateAvgCpuFreq(deltaTimeInState);
- }
- mTimeInStateByPolicy.put(policyDirName, latestTimeInState);
- return calculateAvgCpuFreq(latestTimeInState);
+ private long readAvgTimeInStateCpuFrequency(int policyId, File policyDir) {
+ LongSparseLongArray latestTimeInState = readTimeInState(policyDir);
+ if (latestTimeInState == null || latestTimeInState.size() == 0) {
+ return CpuInfo.MISSING_FREQUENCY;
+ }
+ LongSparseLongArray prevTimeInState = mTimeInStateByPolicyId.get(policyId);
+ if (prevTimeInState == null) {
+ mTimeInStateByPolicyId.put(policyId, latestTimeInState);
+ if (DEBUG) {
+ Slogf.d(TAG, "Added aggregated time in state info for policy id %d", policyId);
+ }
+ return calculateAvgCpuFreq(latestTimeInState);
+ }
+ LongSparseLongArray deltaTimeInState = calculateDeltaTimeInState(prevTimeInState,
+ latestTimeInState);
+ mTimeInStateByPolicyId.put(policyId, latestTimeInState);
+ if (DEBUG) {
+ Slogf.d(TAG, "Added latest delta time in state info for policy id %d", policyId);
+ }
+ return calculateAvgCpuFreq(deltaTimeInState);
}
@Nullable
- private ArrayMap<Long, Long> readTimeInState(File policyDir) {
+ private LongSparseLongArray readTimeInState(File policyDir) {
if (!mHasTimeInStateFile) {
return null;
}
@@ -296,14 +443,14 @@ public final class CpuInfoReader {
Slogf.w(TAG, "Empty time in state file at %s", timeInStateFile.getAbsolutePath());
return null;
}
- ArrayMap<Long, Long> cpuTimeByFrequencies = new ArrayMap<>();
+ LongSparseLongArray cpuTimeByFrequencies = new LongSparseLongArray();
for (int i = 0; i < lines.size(); i++) {
Matcher m = TIME_IN_STATE_PATTERN.matcher(lines.get(i).trim());
if (!m.find()) {
continue;
}
cpuTimeByFrequencies.put(Long.parseLong(m.group("freqKHz")),
- jiffyStrToMillis(m.group("time")));
+ clockTickStrToMillis(m.group("time")));
}
return cpuTimeByFrequencies;
} catch (Exception e) {
@@ -316,40 +463,35 @@ public final class CpuInfoReader {
private static long readCpuFreqKHz(File file) {
if (!file.exists()) {
Slogf.e(TAG, "CPU frequency file %s doesn't exist", file.getAbsolutePath());
- return 0;
+ return CpuInfo.MISSING_FREQUENCY;
}
try {
List<String> lines = Files.readAllLines(file.toPath());
if (!lines.isEmpty()) {
long frequency = Long.parseLong(lines.get(0).trim());
- return frequency > 0 ? frequency : 0;
+ return frequency > 0 ? frequency : CpuInfo.MISSING_FREQUENCY;
}
} catch (Exception e) {
Slogf.e(TAG, e, "Failed to read integer content from file: %s", file.getAbsolutePath());
}
- return 0;
+ return CpuInfo.MISSING_FREQUENCY;
}
- private static ArrayMap<Long, Long> calculateDeltaTimeInState(
- ArrayMap<Long, Long> prevTimeInState, ArrayMap<Long, Long> latestTimeInState) {
- ArrayMap<Long, Long> deltaTimeInState = new ArrayMap();
- for (int i = 0; i < latestTimeInState.size(); i++) {
+ private static LongSparseLongArray calculateDeltaTimeInState(
+ LongSparseLongArray prevTimeInState, LongSparseLongArray latestTimeInState) {
+ int numTimeInStateEntries = latestTimeInState.size();
+ LongSparseLongArray deltaTimeInState = new LongSparseLongArray(numTimeInStateEntries);
+ for (int i = 0; i < numTimeInStateEntries; i++) {
long freq = latestTimeInState.keyAt(i);
long durationMillis = latestTimeInState.valueAt(i);
- long deltaDurationMillis;
- if (prevTimeInState.containsKey(freq)) {
- long prevDurationMillis = prevTimeInState.get(freq);
- deltaDurationMillis = durationMillis > prevDurationMillis
- ? (durationMillis - prevDurationMillis) : durationMillis;
- } else {
- deltaDurationMillis = durationMillis;
- }
- deltaTimeInState.put(freq, deltaDurationMillis);
+ long prevDurationMillis = prevTimeInState.get(freq);
+ deltaTimeInState.put(freq, durationMillis > prevDurationMillis
+ ? (durationMillis - prevDurationMillis) : durationMillis);
}
return deltaTimeInState;
}
- private static long calculateAvgCpuFreq(ArrayMap<Long, Long> timeInState) {
+ private static long calculateAvgCpuFreq(LongSparseLongArray timeInState) {
double totalTimeInState = 0;
for (int i = 0; i < timeInState.size(); i++) {
totalTimeInState += timeInState.valueAt(i);
@@ -364,24 +506,27 @@ public final class CpuInfoReader {
/**
* Reads the list of CPU cores from the given file.
*
- * Reads CPU cores represented in one of the below formats.
+ * <p>Reads CPU cores represented in one of the below formats.
* <ul>
* <li> Single core id. Eg: 1
* <li> Core id range. Eg: 1-4
* <li> Comma separated values. Eg: 1, 3-5, 7
* </ul>
*/
- private static List<Integer> readCpuCores(File file) {
+ @Nullable
+ private static IntArray readCpuCores(File file) {
if (!file.exists()) {
Slogf.e(TAG, "Failed to read CPU cores as the file '%s' doesn't exist",
file.getAbsolutePath());
- return Collections.emptyList();
+ return null;
}
try {
List<String> lines = Files.readAllLines(file.toPath());
- List<Integer> cpuCores = new ArrayList<>();
+ IntArray cpuCores = new IntArray(0);
for (int i = 0; i < lines.size(); i++) {
- String[] pairs = lines.get(i).trim().split(",");
+ String line = lines.get(i);
+ String[] pairs = line.contains(",") ? line.trim().split(",")
+ : line.trim().split(" ");
for (int j = 0; j < pairs.length; j++) {
String[] minMaxPairs = pairs[j].split("-");
if (minMaxPairs.length >= 2) {
@@ -404,7 +549,7 @@ public final class CpuInfoReader {
} catch (Exception e) {
Slogf.e(TAG, e, "Failed to read CPU cores from %s", file.getAbsolutePath());
}
- return Collections.emptyList();
+ return null;
}
@Nullable
@@ -435,16 +580,16 @@ public final class CpuInfoReader {
continue;
}
cpuUsageStats.append(Integer.parseInt(m.group("core")),
- new CpuUsageStats(jiffyStrToMillis(m.group("userClockTicks")),
- jiffyStrToMillis(m.group("niceClockTicks")),
- jiffyStrToMillis(m.group("sysClockTicks")),
- jiffyStrToMillis(m.group("idleClockTicks")),
- jiffyStrToMillis(m.group("iowaitClockTicks")),
- jiffyStrToMillis(m.group("irqClockTicks")),
- jiffyStrToMillis(m.group("softirqClockTicks")),
- jiffyStrToMillis(m.group("stealClockTicks")),
- jiffyStrToMillis(m.group("guestClockTicks")),
- jiffyStrToMillis(m.group("guestNiceClockTicks"))));
+ new CpuUsageStats(clockTickStrToMillis(m.group("userClockTicks")),
+ clockTickStrToMillis(m.group("niceClockTicks")),
+ clockTickStrToMillis(m.group("sysClockTicks")),
+ clockTickStrToMillis(m.group("idleClockTicks")),
+ clockTickStrToMillis(m.group("iowaitClockTicks")),
+ clockTickStrToMillis(m.group("irqClockTicks")),
+ clockTickStrToMillis(m.group("softirqClockTicks")),
+ clockTickStrToMillis(m.group("stealClockTicks")),
+ clockTickStrToMillis(m.group("guestClockTicks")),
+ clockTickStrToMillis(m.group("guestNiceClockTicks"))));
}
} catch (Exception e) {
Slogf.e(TAG, e, "Failed to read cpu usage stats from %s",
@@ -453,33 +598,64 @@ public final class CpuInfoReader {
return cpuUsageStats;
}
- private static long jiffyStrToMillis(String jiffyStr) {
- return Long.parseLong(jiffyStr) * MILLIS_PER_JIFFY;
+ private static long clockTickStrToMillis(String jiffyStr) {
+ return Long.parseLong(jiffyStr) * MILLIS_PER_CLOCK_TICK;
+ }
+
+ private static String toCpusetCategoriesStr(int cpusetCategories) {
+ StringBuilder builder = new StringBuilder();
+ if ((cpusetCategories & FLAG_CPUSET_CATEGORY_TOP_APP) != 0) {
+ builder.append("FLAG_CPUSET_CATEGORY_TOP_APP");
+ }
+ if ((cpusetCategories & FLAG_CPUSET_CATEGORY_BACKGROUND) != 0) {
+ if (builder.length() > 0) {
+ builder.append('|');
+ }
+ builder.append("FLAG_CPUSET_CATEGORY_BACKGROUND");
+ }
+ return builder.toString();
}
/** Contains information for each CPU core on the system. */
public static final class CpuInfo {
+ public static final long MISSING_FREQUENCY = 0;
+
public final int cpuCore;
- public final @CpusetCategory int cpusetCategories;
+ @CpusetCategory
+ public final int cpusetCategories;
+ public final boolean isOnline;
+ // Values in the below fields may be missing when a CPU core is offline.
public final long curCpuFreqKHz;
public final long maxCpuFreqKHz;
+ public final long avgTimeInStateCpuFreqKHz;
+ @Nullable
public final CpuUsageStats latestCpuUsageStats;
- CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, long curCpuFreqKHz,
- long maxCpuFreqKHz, CpuUsageStats latestCpuUsageStats) {
+ CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, boolean isOnline,
+ long curCpuFreqKHz, long maxCpuFreqKHz, long avgTimeInStateCpuFreqKHz,
+ CpuUsageStats latestCpuUsageStats) {
this.cpuCore = cpuCore;
this.cpusetCategories = cpusetCategories;
+ this.isOnline = isOnline;
this.curCpuFreqKHz = curCpuFreqKHz;
this.maxCpuFreqKHz = maxCpuFreqKHz;
+ this.avgTimeInStateCpuFreqKHz = avgTimeInStateCpuFreqKHz;
this.latestCpuUsageStats = latestCpuUsageStats;
}
@Override
public String toString() {
return new StringBuilder("CpuInfo{ cpuCore = ").append(cpuCore)
- .append(", cpusetCategories = ").append(cpusetCategories)
- .append(", curCpuFreqKHz = ").append(curCpuFreqKHz)
- .append(", maxCpuFreqKHz = ").append(maxCpuFreqKHz)
+ .append(", cpusetCategories = [")
+ .append(toCpusetCategoriesStr(cpusetCategories))
+ .append("], isOnline = ").append(isOnline ? "Yes" : "No")
+ .append(", curCpuFreqKHz = ")
+ .append(curCpuFreqKHz == MISSING_FREQUENCY ? "missing" : curCpuFreqKHz)
+ .append(", maxCpuFreqKHz = ")
+ .append(maxCpuFreqKHz == MISSING_FREQUENCY ? "missing" : maxCpuFreqKHz)
+ .append(", avgTimeInStateCpuFreqKHz = ")
+ .append(avgTimeInStateCpuFreqKHz == MISSING_FREQUENCY ? "missing"
+ : avgTimeInStateCpuFreqKHz)
.append(", latestCpuUsageStats = ").append(latestCpuUsageStats)
.append(" }").toString();
}
@@ -494,15 +670,16 @@ public final class CpuInfoReader {
}
CpuInfo other = (CpuInfo) obj;
return cpuCore == other.cpuCore && cpusetCategories == other.cpusetCategories
- && curCpuFreqKHz == other.curCpuFreqKHz
+ && isOnline == other.isOnline && curCpuFreqKHz == other.curCpuFreqKHz
&& maxCpuFreqKHz == other.maxCpuFreqKHz
+ && avgTimeInStateCpuFreqKHz == other.avgTimeInStateCpuFreqKHz
&& latestCpuUsageStats.equals(other.latestCpuUsageStats);
}
@Override
public int hashCode() {
- return Objects.hash(cpuCore, cpusetCategories, curCpuFreqKHz, maxCpuFreqKHz,
- latestCpuUsageStats);
+ return Objects.hash(cpuCore, cpusetCategories, isOnline, curCpuFreqKHz, maxCpuFreqKHz,
+ avgTimeInStateCpuFreqKHz, latestCpuUsageStats);
}
}
@@ -602,4 +779,61 @@ public final class CpuInfoReader {
return lhs > rhs ? lhs - rhs : 0;
}
}
+
+ private static final class FrequencyPair {
+ public final long cpuFreqKHz;
+ public final long scalingFreqKHz;
+
+ FrequencyPair(long cpuFreqKHz, long scalingFreqKHz) {
+ this.cpuFreqKHz = cpuFreqKHz;
+ this.scalingFreqKHz = scalingFreqKHz;
+ }
+
+ boolean isEmpty() {
+ return cpuFreqKHz == CpuInfo.MISSING_FREQUENCY
+ && scalingFreqKHz == CpuInfo.MISSING_FREQUENCY;
+ }
+
+ @Override
+ public String toString() {
+ return "FrequencyPair{cpuFreqKHz=" + cpuFreqKHz + ", scalingFreqKHz=" + scalingFreqKHz
+ + '}';
+ }
+ }
+
+ private static final class StaticPolicyInfo {
+ public final FrequencyPair maxCpuFreqPair;
+ public final IntArray relatedCpuCores;
+
+ StaticPolicyInfo(FrequencyPair maxCpuFreqPair, IntArray relatedCpuCores) {
+ this.maxCpuFreqPair = maxCpuFreqPair;
+ this.relatedCpuCores = relatedCpuCores;
+ }
+
+ @Override
+ public String toString() {
+ return "StaticPolicyInfo{maxCpuFreqPair=" + maxCpuFreqPair + ", relatedCpuCores="
+ + relatedCpuCores + '}';
+ }
+ }
+
+ private static final class DynamicPolicyInfo {
+ public final FrequencyPair curCpuFreqPair;
+ public final long avgTimeInStateCpuFreqKHz;
+ public final IntArray affectedCpuCores;
+
+ DynamicPolicyInfo(FrequencyPair curCpuFreqPair, long avgTimeInStateCpuFreqKHz,
+ IntArray affectedCpuCores) {
+ this.curCpuFreqPair = curCpuFreqPair;
+ this.avgTimeInStateCpuFreqKHz = avgTimeInStateCpuFreqKHz;
+ this.affectedCpuCores = affectedCpuCores;
+ }
+
+ @Override
+ public String toString() {
+ return "DynamicPolicyInfo{curCpuFreqPair=" + curCpuFreqPair
+ + ", avgTimeInStateCpuFreqKHz=" + avgTimeInStateCpuFreqKHz
+ + ", affectedCpuCores=" + affectedCpuCores + '}';
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 98a0af7f7c15..1217e7423634 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -46,6 +46,7 @@ import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
import com.android.server.display.config.HighBrightnessMode;
+import com.android.server.display.config.IntegerArray;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.Point;
import com.android.server.display.config.RefreshRateConfigs;
@@ -213,6 +214,10 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <type>android.sensor.light</type>
* <name>1234 Ambient Light Sensor</name>
* </lightSensor>
+ * <screenOffBrightnessSensor>
+ * <type>com.google.sensor.binned_brightness</type>
+ * <name>Binned Brightness 0 (wake-up)</name>
+ * </screenOffBrightnessSensor>
* <proxSensor>
* <type>android.sensor.proximity</type>
* <name>1234 Proximity Sensor</name>
@@ -368,6 +373,13 @@ import javax.xml.datatype.DatatypeConfigurationException;
* </brightnessThresholdPoints>
* </darkeningThresholds>
* </displayBrightnessChangeThresholdsIdle>
+ * <screenOffBrightnessSensorValueToLux>
+ * <item>-1</item>
+ * <item>0</item>
+ * <item>5</item>
+ * <item>80</item>
+ * <item>1500</item>
+ * </screenOffBrightnessSensorValueToLux>
* </displayConfiguration>
* }
* </pre>
@@ -428,6 +440,9 @@ public class DisplayDeviceConfig {
// The details of the ambient light sensor associated with this display.
private final SensorData mAmbientLightSensor = new SensorData();
+ // The details of the doze brightness sensor associated with this display.
+ private final SensorData mScreenOffBrightnessSensor = new SensorData();
+
// The details of the proximity sensor associated with this display.
private final SensorData mProximitySensor = new SensorData();
@@ -523,6 +538,9 @@ public class DisplayDeviceConfig {
private float[] mAmbientDarkeningLevelsIdle = DEFAULT_AMBIENT_THRESHOLD_LEVELS;
private float[] mAmbientDarkeningPercentagesIdle = DEFAULT_AMBIENT_DARKENING_THRESHOLDS;
+ // A mapping between screen off sensor values and lux values
+ private int[] mScreenOffBrightnessSensorValueToLux;
+
private Spline mBrightnessToBacklightSpline;
private Spline mBacklightToBrightnessSpline;
private Spline mBacklightToNitsSpline;
@@ -1197,6 +1215,10 @@ public class DisplayDeviceConfig {
return mAmbientLightSensor;
}
+ SensorData getScreenOffBrightnessSensor() {
+ return mScreenOffBrightnessSensor;
+ }
+
SensorData getProximitySensor() {
return mProximitySensor;
}
@@ -1320,6 +1342,14 @@ public class DisplayDeviceConfig {
return mHighAmbientBrightnessThresholds;
}
+ /**
+ * @return A mapping from screen off brightness sensor readings to lux values. This estimates
+ * the ambient lux when the screen is off to determine the initial brightness
+ */
+ public int[] getScreenOffBrightnessSensorValueToLux() {
+ return mScreenOffBrightnessSensorValueToLux;
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -1398,6 +1428,7 @@ public class DisplayDeviceConfig {
mScreenDarkeningPercentagesIdle)
+ "\n"
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ + ", mScreenOffBrightnessSensor=" + mScreenOffBrightnessSensor
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ ", mDensityMapping= " + mDensityMapping
@@ -1420,6 +1451,9 @@ public class DisplayDeviceConfig {
+ Arrays.toString(mHighDisplayBrightnessThresholds)
+ ", mHighAmbientBrightnessThresholds= "
+ Arrays.toString(mHighAmbientBrightnessThresholds)
+ + "\n"
+ + ", mScreenOffBrightnessSensorValueToLux=" + Arrays.toString(
+ mScreenOffBrightnessSensorValueToLux)
+ "}";
}
@@ -1473,11 +1507,13 @@ public class DisplayDeviceConfig {
loadQuirks(config);
loadBrightnessRamps(config);
loadAmbientLightSensorFromDdc(config);
+ loadScreenOffBrightnessSensorFromDdc(config);
loadProxSensorFromDdc(config);
loadAmbientHorizonFromDdc(config);
loadBrightnessChangeThresholds(config);
loadAutoBrightnessConfigValues(config);
loadRefreshRateSetting(config);
+ loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -2169,6 +2205,14 @@ public class DisplayDeviceConfig {
mProximitySensor.type = null;
}
+ private void loadScreenOffBrightnessSensorFromDdc(DisplayConfiguration config) {
+ final SensorDetails sensorDetails = config.getScreenOffBrightnessSensor();
+ if (sensorDetails != null) {
+ mScreenOffBrightnessSensor.type = sensorDetails.getType();
+ mScreenOffBrightnessSensor.name = sensorDetails.getName();
+ }
+ }
+
private void loadProxSensorFromDdc(DisplayConfiguration config) {
SensorDetails sensorDetails = config.getProxSensor();
if (sensorDetails != null) {
@@ -2573,6 +2617,19 @@ public class DisplayDeviceConfig {
&& mDdcAutoBrightnessAvailable;
}
+ private void loadScreenOffBrightnessSensorValueToLuxFromDdc(DisplayConfiguration config) {
+ IntegerArray sensorValueToLux = config.getScreenOffBrightnessSensorValueToLux();
+ if (sensorValueToLux == null) {
+ return;
+ }
+
+ List<BigInteger> items = sensorValueToLux.getItem();
+ mScreenOffBrightnessSensorValueToLux = new int[items.size()];
+ for (int i = 0; i < items.size(); i++) {
+ mScreenOffBrightnessSensorValueToLux[i] = items.get(i).intValue();
+ }
+ }
+
static class SensorData {
public String type;
public String name;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 75415cd9997f..e4fcdbd07188 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -408,7 +408,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Nullable
private AutomaticBrightnessController mAutomaticBrightnessController;
+ // The controller for the sensor used to estimate ambient lux while the display is off.
+ @Nullable
+ private ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
+
private Sensor mLightSensor;
+ private Sensor mScreenOffBrightnessSensor;
// The mappers between ambient lux, display backlight values, and display brightness.
// We will switch between the idle mapper and active mapper in AutomaticBrightnessController.
@@ -1077,6 +1082,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mBrightnessEventRingBuffer =
new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX);
+
+ loadScreenOffBrightnessSensor();
+ int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
+ if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
+ mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
+ mSensorManager,
+ mScreenOffBrightnessSensor,
+ mHandler,
+ SystemClock::uptimeMillis,
+ sensorValueToLux,
+ mInteractiveModeBrightnessMapper
+ );
+ }
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -1262,6 +1280,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
assert (state != Display.STATE_UNKNOWN);
+ if (mScreenOffBrightnessSensorController != null) {
+ mScreenOffBrightnessSensorController.setLightSensorEnabled(mUseAutoBrightness
+ && (state == Display.STATE_OFF || (state == Display.STATE_DOZE
+ && !mAllowAutoBrightnessWhileDozingConfig)));
+ }
+
boolean skipRampBecauseOfProximityChangeToNegative = false;
// Apply the proximity sensor.
if (mProximitySensor != null) {
@@ -1433,6 +1457,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
mAppliedAutoBrightness = true;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
+ if (mScreenOffBrightnessSensorController != null) {
+ mScreenOffBrightnessSensorController.setLightSensorEnabled(false);
+ }
} else {
mAppliedAutoBrightness = false;
}
@@ -1460,6 +1487,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
}
+ // The ALS is not available yet - use the screen off sensor to determine the initial
+ // brightness
+ if (Float.isNaN(brightnessState) && autoBrightnessEnabled
+ && mScreenOffBrightnessSensorController != null) {
+ brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ if (isValidBrightnessValue(brightnessState)) {
+ brightnessState = clampScreenBrightness(brightnessState);
+ updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
+ mBrightnessReasonTemp.setReason(
+ BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR);
+ }
+ }
+
// Apply manual brightness.
if (Float.isNaN(brightnessState)) {
brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
@@ -2034,6 +2074,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
fallbackType);
}
+ private void loadScreenOffBrightnessSensor() {
+ DisplayDeviceConfig.SensorData screenOffBrightnessSensor =
+ mDisplayDeviceConfig.getScreenOffBrightnessSensor();
+ mScreenOffBrightnessSensor = SensorUtils.findSensor(mSensorManager,
+ screenOffBrightnessSensor.type, screenOffBrightnessSensor.name,
+ SensorUtils.NO_FALLBACK);
+ }
+
private void loadProximitySensor() {
if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
return;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 111caefa34da..81011dcb5bc7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -360,7 +360,12 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
@Nullable
private AutomaticBrightnessController mAutomaticBrightnessController;
+ // The controller for the sensor used to estimate ambient lux while the display is off.
+ @Nullable
+ private ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
+
private Sensor mLightSensor;
+ private Sensor mScreenOffBrightnessSensor;
// The mappers between ambient lux, display backlight values, and display brightness.
// We will switch between the idle mapper and active mapper in AutomaticBrightnessController.
@@ -994,6 +999,19 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mBrightnessEventRingBuffer =
new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX);
+
+ loadScreenOffBrightnessSensor();
+ int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
+ if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
+ mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
+ mSensorManager,
+ mScreenOffBrightnessSensor,
+ mHandler,
+ SystemClock::uptimeMillis,
+ sensorValueToLux,
+ mInteractiveModeBrightnessMapper
+ );
+ }
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -1135,6 +1153,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
int state = mDisplayStateController
.updateDisplayState(mPowerRequest, mIsEnabled, mIsInTransition);
+
+ if (mScreenOffBrightnessSensorController != null) {
+ mScreenOffBrightnessSensorController.setLightSensorEnabled(mUseAutoBrightness
+ && (state == Display.STATE_OFF || (state == Display.STATE_DOZE
+ && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig())));
+ }
+
// Initialize things the first time the power state is changed.
if (mustInitialize) {
initialize(state);
@@ -1224,6 +1249,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
mAppliedAutoBrightness = true;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
+ if (mScreenOffBrightnessSensorController != null) {
+ mScreenOffBrightnessSensorController.setLightSensorEnabled(false);
+ }
} else {
mAppliedAutoBrightness = false;
}
@@ -1251,6 +1279,19 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
}
+ // The ALS is not available yet - use the screen off sensor to determine the initial
+ // brightness
+ if (Float.isNaN(brightnessState) && autoBrightnessEnabled
+ && mScreenOffBrightnessSensorController != null) {
+ brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ if (isValidBrightnessValue(brightnessState)) {
+ brightnessState = clampScreenBrightness(brightnessState);
+ updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
+ mBrightnessReasonTemp.setReason(
+ BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR);
+ }
+ }
+
// Apply manual brightness.
if (Float.isNaN(brightnessState)) {
brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
@@ -1819,6 +1860,14 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
fallbackType);
}
+ private void loadScreenOffBrightnessSensor() {
+ DisplayDeviceConfig.SensorData screenOffBrightnessSensor =
+ mDisplayDeviceConfig.getScreenOffBrightnessSensor();
+ mScreenOffBrightnessSensor = SensorUtils.findSensor(mSensorManager,
+ screenOffBrightnessSensor.type, screenOffBrightnessSensor.name,
+ SensorUtils.NO_FALLBACK);
+ }
+
private float clampScreenBrightness(float value) {
if (Float.isNaN(value)) {
value = PowerManager.BRIGHTNESS_MIN;
diff --git a/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
new file mode 100644
index 000000000000..6f50dac07b99
--- /dev/null
+++ b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
@@ -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.server.display;
+
+import android.annotation.Nullable;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * Controls the light sensor when the screen is off. The sensor used here does not report lux values
+ * but an index that needs to be mapped to a lux value.
+ */
+public class ScreenOffBrightnessSensorController implements SensorEventListener {
+ private static final String TAG = "ScreenOffBrightnessSensorController";
+
+ private static final int SENSOR_INVALID_VALUE = -1;
+ private static final long SENSOR_VALUE_VALID_TIME_MILLIS = 1500;
+
+ private final Handler mHandler;
+ private final Clock mClock;
+ private final SensorManager mSensorManager;
+ private final Sensor mLightSensor;
+ private final int[] mSensorValueToLux;
+
+ private boolean mRegistered;
+ private int mLastSensorValue = SENSOR_INVALID_VALUE;
+ private long mSensorDisableTime = -1;
+
+ // The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
+ @Nullable
+ private final BrightnessMappingStrategy mBrightnessMapper;
+
+ public ScreenOffBrightnessSensorController(
+ SensorManager sensorManager,
+ Sensor lightSensor,
+ Handler handler,
+ Clock clock,
+ int[] sensorValueToLux,
+ BrightnessMappingStrategy brightnessMapper) {
+ mSensorManager = sensorManager;
+ mLightSensor = lightSensor;
+ mHandler = handler;
+ mClock = clock;
+ mSensorValueToLux = sensorValueToLux;
+ mBrightnessMapper = brightnessMapper;
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mRegistered) {
+ mLastSensorValue = (int) event.values[0];
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+
+ void setLightSensorEnabled(boolean enabled) {
+ if (enabled && !mRegistered) {
+ // Wait until we get an event from the sensor indicating ready.
+ mRegistered = mSensorManager.registerListener(this, mLightSensor,
+ SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+ mLastSensorValue = SENSOR_INVALID_VALUE;
+ } else if (!enabled && mRegistered) {
+ mSensorManager.unregisterListener(this);
+ mRegistered = false;
+ mSensorDisableTime = mClock.uptimeMillis();
+ }
+ }
+
+ float getAutomaticScreenBrightness() {
+ if (mLastSensorValue < 0 || mLastSensorValue >= mSensorValueToLux.length
+ || (!mRegistered
+ && mClock.uptimeMillis() - mSensorDisableTime > SENSOR_VALUE_VALID_TIME_MILLIS)) {
+ return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
+
+ int lux = mSensorValueToLux[mLastSensorValue];
+ if (lux < 0) {
+ return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
+
+ return mBrightnessMapper.getBrightness(lux);
+ }
+
+ /** Dump current state */
+ public void dump(PrintWriter pw) {
+ pw.println("ScreenOffBrightnessSensorController:");
+ IndentingPrintWriter idpw = new IndentingPrintWriter(pw);
+ idpw.increaseIndent();
+ idpw.println("registered=" + mRegistered);
+ idpw.println("lastSensorValue=" + mLastSensorValue);
+ }
+
+ /** Functional interface for providing time. */
+ @VisibleForTesting
+ interface Clock {
+ /**
+ * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+ */
+ long uptimeMillis();
+ }
+}
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index b6be713d344b..a952004e7f9d 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -37,7 +37,8 @@ public final class BrightnessReason {
public static final int REASON_OVERRIDE = 6;
public static final int REASON_TEMPORARY = 7;
public static final int REASON_BOOST = 8;
- public static final int REASON_MAX = REASON_BOOST;
+ public static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 9;
+ public static final int REASON_MAX = REASON_SCREEN_OFF_BRIGHTNESS_SENSOR;
public static final int MODIFIER_DIMMED = 0x1;
public static final int MODIFIER_LOW_POWER = 0x2;
@@ -190,6 +191,8 @@ public final class BrightnessReason {
return "temporary";
case REASON_BOOST:
return "boost";
+ case REASON_SCREEN_OFF_BRIGHTNESS_SENSOR:
+ return "screen_off_brightness_sensor";
default:
return Integer.toString(reason);
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index b8225419cb9b..d510523848d3 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -55,6 +55,7 @@ import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.service.dreams.DreamService;
@@ -113,6 +114,7 @@ public final class DreamManagerService extends SystemService {
private final PowerManagerInternal mPowerManagerInternal;
private final PowerManager.WakeLock mDozeWakeLock;
private final ActivityTaskManagerInternal mAtmInternal;
+ private final UserManager mUserManager;
private final UiEventLogger mUiEventLogger;
private final DreamUiEventLogger mDreamUiEventLogger;
private final ComponentName mAmbientDisplayComponent;
@@ -212,6 +214,7 @@ public final class DreamManagerService extends SystemService {
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
mAtmInternal = getLocalService(ActivityTaskManagerInternal.class);
+ mUserManager = context.getSystemService(UserManager.class);
mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, DOZE_WAKE_LOCK_TAG);
mDozeConfig = new AmbientDisplayConfiguration(mContext);
mUiEventLogger = new UiEventLoggerImpl();
@@ -382,6 +385,10 @@ public final class DreamManagerService extends SystemService {
return false;
}
+ if (!mUserManager.isUserUnlocked()) {
+ return false;
+ }
+
if ((mWhenToDream & DREAM_ON_CHARGE) == DREAM_ON_CHARGE) {
return mIsCharging;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 50edd0e8e008..f3c67fb9bbfb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -1499,11 +1499,13 @@ final class HdmiCecController {
}
final class HdmiCecCallback {
+ @VisibleForTesting
public void onCecMessage(int initiator, int destination, byte[] body) {
runOnServiceThread(
() -> handleIncomingCecCommand(initiator, destination, body));
}
+ @VisibleForTesting
public void onHotplugEvent(int portId, boolean connected) {
runOnServiceThread(() -> handleHotplug(portId, connected));
}
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 01a564d6816f..30f6145f77e3 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -186,4 +186,24 @@ public abstract class InputManagerInternal {
* @param inputPort The port of the input device.
*/
public abstract void unsetTypeAssociation(@NonNull String inputPort);
+
+ /**
+ * Add a mapping from the input port and a keyboard layout, by unique id. Input
+ * ports are expected to be unique.
+ *
+ * @param inputPort The port of the input device.
+ * @param languageTag the language of the input device as an IETF
+ * <a href="https://tools.ietf.org/html/bcp47">BCP-47</a>
+ * conformant tag.
+ * @param layoutType the layout type such as "qwerty" or "azerty".
+ */
+ public abstract void addKeyboardLayoutAssociation(@NonNull String inputPort,
+ @NonNull String languageTag, @NonNull String layoutType);
+
+ /**
+ * Removes the mapping from input port to the keyboard layout identifier.
+ *
+ * @param inputPort The port of the input device.
+ */
+ public abstract void removeKeyboardLayoutAssociation(@NonNull String inputPort);
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 1809b1821b5e..e1e99a1cb3b7 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -247,6 +247,11 @@ public class InputManagerService extends IInputManager.Stub
private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<>();
@GuardedBy("mAssociationsLock")
private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>();
+ // The map from input port (String) to the keyboard layout identifiers (comma separated string
+ // containing language tag and layout type) associated with the corresponding keyboard device.
+ // Currently only accessed by InputReader.
+ @GuardedBy("mAssociationsLock")
+ private final Map<String, String> mKeyboardLayoutAssociations = new ArrayMap<>();
// Stores input ports associated with device types. For example, adding an association
// {"123", "touchNavigation"} here would mean that a touch device appearing at port "123" would
@@ -1901,8 +1906,7 @@ public class InputManagerService extends IInputManager.Stub
if (!checkCallingPermission(
android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
"removeUniqueIdAssociation()")) {
- throw new SecurityException(
- "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
+ throw new SecurityException("Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
}
Objects.requireNonNull(inputPort);
@@ -1929,6 +1933,27 @@ public class InputManagerService extends IInputManager.Stub
mNative.changeTypeAssociation();
}
+ private void addKeyboardLayoutAssociation(@NonNull String inputPort,
+ @NonNull String languageTag, @NonNull String layoutType) {
+ Objects.requireNonNull(inputPort);
+ Objects.requireNonNull(languageTag);
+ Objects.requireNonNull(layoutType);
+
+ synchronized (mAssociationsLock) {
+ mKeyboardLayoutAssociations.put(inputPort,
+ TextUtils.formatSimple("%s,%s", languageTag, layoutType));
+ }
+ mNative.changeKeyboardLayoutAssociation();
+ }
+
+ private void removeKeyboardLayoutAssociation(@NonNull String inputPort) {
+ Objects.requireNonNull(inputPort);
+ synchronized (mAssociationsLock) {
+ mKeyboardLayoutAssociations.remove(inputPort);
+ }
+ mNative.changeKeyboardLayoutAssociation();
+ }
+
@Override // Binder call
public InputSensorInfo[] getSensorList(int deviceId) {
return mNative.getSensorList(deviceId);
@@ -2673,6 +2698,17 @@ public class InputManagerService extends IInputManager.Stub
return flatten(associations);
}
+ // Native callback
+ @SuppressWarnings("unused")
+ @VisibleForTesting
+ private String[] getKeyboardLayoutAssociations() {
+ final Map<String, String> configs = new ArrayMap<>();
+ synchronized (mAssociationsLock) {
+ configs.putAll(mKeyboardLayoutAssociations);
+ }
+ return flatten(configs);
+ }
+
/**
* Gets if an input device could dispatch to the given display".
* @param deviceId The input device id.
@@ -3316,6 +3352,18 @@ public class InputManagerService extends IInputManager.Stub
public void unsetTypeAssociation(@NonNull String inputPort) {
unsetTypeAssociationInternal(inputPort);
}
+
+ @Override
+ public void addKeyboardLayoutAssociation(@NonNull String inputPort,
+ @NonNull String languageTag, @NonNull String layoutType) {
+ InputManagerService.this.addKeyboardLayoutAssociation(inputPort,
+ languageTag, layoutType);
+ }
+
+ @Override
+ public void removeKeyboardLayoutAssociation(@NonNull String inputPort) {
+ InputManagerService.this.removeKeyboardLayoutAssociation(inputPort);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 184bc0e3519d..157c957ee817 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -188,6 +188,8 @@ interface NativeInputManagerService {
void changeTypeAssociation();
+ void changeKeyboardLayoutAssociation();
+
void notifyPointerDisplayIdChanged();
void setDisplayEligibilityForPointerCapture(int displayId, boolean enabled);
@@ -405,6 +407,9 @@ interface NativeInputManagerService {
public native void changeTypeAssociation();
@Override
+ public native void changeKeyboardLayoutAssociation();
+
+ @Override
public native void notifyPointerDisplayIdChanged();
@Override
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
index c69934aceb75..7a96195528d5 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
@@ -21,18 +21,16 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
-import android.util.Pair;
+import android.util.Slog;
import com.android.internal.util.HexDump;
import java.io.File;
-import java.util.ArrayList;
-import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
/**
@@ -132,7 +130,13 @@ class WatchlistReportDbHelper extends SQLiteOpenHelper {
*/
public boolean insertNewRecord(byte[] appDigest, String cncDomain,
long timestamp) {
- final SQLiteDatabase db = getWritableDatabase();
+ final SQLiteDatabase db;
+ try {
+ db = getWritableDatabase();
+ } catch (SQLiteException e) {
+ Slog.e(TAG, "Error opening the database to insert a new record", e);
+ return false;
+ }
final ContentValues values = new ContentValues();
values.put(WhiteListReportContract.APP_DIGEST, appDigest);
values.put(WhiteListReportContract.CNC_DOMAIN, cncDomain);
@@ -148,7 +152,13 @@ class WatchlistReportDbHelper extends SQLiteOpenHelper {
public AggregatedResult getAggregatedRecords(long untilTimestamp) {
final String selectStatement = WhiteListReportContract.TIMESTAMP + " < ?";
- final SQLiteDatabase db = getReadableDatabase();
+ final SQLiteDatabase db;
+ try {
+ db = getReadableDatabase();
+ } catch (SQLiteException e) {
+ Slog.e(TAG, "Error opening the database", e);
+ return null;
+ }
Cursor c = null;
try {
c = db.query(true /* distinct */,
@@ -186,8 +196,14 @@ class WatchlistReportDbHelper extends SQLiteOpenHelper {
* @return True if success.
*/
public boolean cleanup(long untilTimestamp) {
- final SQLiteDatabase db = getWritableDatabase();
+ final SQLiteDatabase db;
+ try {
+ db = getWritableDatabase();
+ } catch (SQLiteException e) {
+ Slog.e(TAG, "Error opening the database to cleanup", e);
+ return false;
+ }
final String clause = WhiteListReportContract.TIMESTAMP + "< " + untilTimestamp;
return db.delete(WhiteListReportContract.TABLE, clause, null) != 0;
}
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index d4c4c694a6c5..db3a3434388e 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -22,8 +22,6 @@ import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IBackgroundInstallControlService;
-import android.content.pm.IPackageManager;
-import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -32,8 +30,6 @@ import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -54,6 +50,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.TreeSet;
@@ -75,7 +72,7 @@ public class BackgroundInstallControlService extends SystemService {
private final Context mContext;
private final BinderService mBinderService;
- private final IPackageManager mIPackageManager;
+ private final PackageManager mPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
private final UsageStatsManagerInternal mUsageStatsManagerInternal;
private final PermissionManagerServiceInternal mPermissionManager;
@@ -98,7 +95,7 @@ public class BackgroundInstallControlService extends SystemService {
BackgroundInstallControlService(@NonNull Injector injector) {
super(injector.getContext());
mContext = injector.getContext();
- mIPackageManager = injector.getIPackageManager();
+ mPackageManager = injector.getPackageManager();
mPackageManagerInternal = injector.getPackageManagerInternal();
mPermissionManager = injector.getPermissionManager();
mHandler = new EventHandler(injector.getLooper(), this);
@@ -131,16 +128,12 @@ public class BackgroundInstallControlService extends SystemService {
@VisibleForTesting
ParceledListSlice<PackageInfo> getBackgroundInstalledPackages(
@PackageManager.PackageInfoFlagsBits long flags, int userId) {
- ParceledListSlice<PackageInfo> packages;
- try {
- packages = mIPackageManager.getInstalledPackages(flags, userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.PackageInfoFlags.of(flags), userId);
initBackgroundInstalledPackages();
- ListIterator<PackageInfo> iter = packages.getList().listIterator();
+ ListIterator<PackageInfo> iter = packages.listIterator();
while (iter.hasNext()) {
String packageName = iter.next().packageName;
if (!mBackgroundInstalledPackages.contains(userId, packageName)) {
@@ -148,7 +141,7 @@ public class BackgroundInstallControlService extends SystemService {
}
}
- return packages;
+ return new ParceledListSlice<>(packages);
}
private static class EventHandler extends Handler {
@@ -181,31 +174,21 @@ public class BackgroundInstallControlService extends SystemService {
}
void handlePackageAdd(String packageName, int userId) {
- InstallSourceInfo installSourceInfo = null;
+ ApplicationInfo appInfo = null;
try {
- installSourceInfo = mIPackageManager.getInstallSourceInfo(packageName);
- } catch (RemoteException e) {
- // Failed to talk to PackageManagerService Should never happen!
- throw e.rethrowFromSystemServer();
- }
- String installerPackageName =
- installSourceInfo == null ? null : installSourceInfo.getInstallingPackageName();
- if (installerPackageName == null) {
- Slog.w(TAG, "fails to get installerPackageName for " + packageName);
+ appInfo = mPackageManager.getApplicationInfoAsUser(packageName,
+ PackageManager.ApplicationInfoFlags.of(0), userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Package's appInfo not found " + packageName);
return;
}
- ApplicationInfo appInfo = null;
+ String installerPackageName = null;
try {
- appInfo = mIPackageManager.getApplicationInfo(packageName,
- 0, userId);
- } catch (RemoteException e) {
- // Failed to talk to PackageManagerService Should never happen!
- throw e.rethrowFromSystemServer();
- }
-
- if (appInfo == null) {
- Slog.w(TAG, "fails to get appInfo for " + packageName);
+ installerPackageName = mPackageManager
+ .getInstallSourceInfo(packageName).getInstallingPackageName();
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Package's installer not found " + packageName);
return;
}
@@ -486,7 +469,7 @@ public class BackgroundInstallControlService extends SystemService {
interface Injector {
Context getContext();
- IPackageManager getIPackageManager();
+ PackageManager getPackageManager();
PackageManagerInternal getPackageManagerInternal();
@@ -512,8 +495,8 @@ public class BackgroundInstallControlService extends SystemService {
}
@Override
- public IPackageManager getIPackageManager() {
- return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ public PackageManager getPackageManager() {
+ return mContext.getPackageManager();
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9aaf685e9817..f65a65d6762e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -34,6 +34,7 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFIC
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.os.Process.INVALID_UID;
+import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
@@ -48,6 +49,7 @@ import static com.android.internal.util.XmlUtils.writeByteArrayAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
import static com.android.internal.util.XmlUtils.writeUriAttribute;
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
+import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
import android.Manifest;
import android.annotation.AnyThread;
@@ -106,6 +108,7 @@ import android.icu.util.ULocale;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.os.FileBridge;
import android.os.FileUtils;
import android.os.Handler;
@@ -125,6 +128,7 @@ import android.os.incremental.IncrementalManager;
import android.os.incremental.PerUidReadTimeouts;
import android.os.incremental.StorageHealthCheckParams;
import android.os.storage.StorageManager;
+import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.system.ErrnoException;
@@ -178,6 +182,7 @@ import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.file.Files;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.Certificate;
@@ -292,6 +297,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
*/
private static final int INVALID_TARGET_SDK_VERSION = Integer.MAX_VALUE;
+ /**
+ * Byte size limit for app metadata.
+ *
+ * Flag type: {@code long}
+ * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
+ */
+ private static final String PROPERTY_APP_METADATA_BYTE_SIZE_LIMIT =
+ "app_metadata_byte_size_limit";
+
+ /** Default byte size limit for app metadata */
+ private static final long DEFAULT_APP_METADATA_BYTE_SIZE_LIMIT = 32000;
+
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
@@ -708,6 +725,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// entries like "lost+found".
if (file.isDirectory()) return false;
if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
+ if (isAppMetadata(file)) return false;
if (DexMetadataHelper.isDexMetadataFile(file)) return false;
if (VerityUtils.isFsveritySignatureFile(file)) return false;
if (ApkChecksums.isDigestOrDigestSignatureFile(file)) return false;
@@ -1434,6 +1452,61 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private File getTmpAppMetadataFile() {
+ return new File(Environment.getDataAppDirectory(params.volumeUuid),
+ sessionId + "-" + APP_METADATA_FILE_NAME);
+ }
+
+ private File getStagedAppMetadataFile() {
+ File file = new File(stageDir, APP_METADATA_FILE_NAME);
+ return file.exists() ? file : null;
+ }
+
+ private static boolean isAppMetadata(String name) {
+ return name.endsWith(APP_METADATA_FILE_NAME);
+ }
+
+ private static boolean isAppMetadata(File file) {
+ return isAppMetadata(file.getName());
+ }
+
+ @Override
+ public ParcelFileDescriptor getAppMetadataFd() {
+ assertCallerIsOwnerOrRoot();
+ synchronized (mLock) {
+ assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
+ try {
+ return openReadInternalLocked(APP_METADATA_FILE_NAME);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+ }
+
+ private static long getAppMetadataSizeLimit() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+ PROPERTY_APP_METADATA_BYTE_SIZE_LIMIT, DEFAULT_APP_METADATA_BYTE_SIZE_LIMIT);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public ParcelFileDescriptor openWriteAppMetadata() {
+ assertCallerIsOwnerOrRoot();
+ synchronized (mLock) {
+ assertPreparedAndNotSealedLocked("openWriteAppMetadata");
+ }
+ try {
+ return doWriteInternal(APP_METADATA_FILE_NAME, /* offsetBytes= */ 0,
+ /* lengthBytes= */ -1, null);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
@Override
public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
assertCanWrite(false);
@@ -1705,6 +1778,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ File appMetadataFile = getStagedAppMetadataFile();
+ if (appMetadataFile != null) {
+ long sizeLimit = getAppMetadataSizeLimit();
+ if (appMetadataFile.length() > sizeLimit) {
+ appMetadataFile.delete();
+ throw new IllegalArgumentException(
+ "App metadata size exceeds the maximum allowed limit of " + sizeLimit);
+ }
+ if (isIncrementalInstallation()) {
+ // Incremental requires stageDir to be empty so move the app metadata file to a
+ // temporary location and move back after commit.
+ appMetadataFile.renameTo(getTmpAppMetadataFile());
+ }
+ }
+
dispatchSessionSealed();
}
@@ -2802,7 +2890,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
final List<File> addedFiles = getAddedApksLocked();
- if (addedFiles.isEmpty() && removeSplitList.size() == 0) {
+ if (addedFiles.isEmpty()
+ && (removeSplitList.size() == 0 || getStagedAppMetadataFile() != null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
TextUtils.formatSimple("Session: %d. No packages staged in %s", sessionId,
stageDir.getAbsolutePath()));
@@ -2899,10 +2988,27 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- if (isIncrementalInstallation() && !isIncrementalInstallationAllowed(mPackageName)) {
- throw new PackageManagerException(
- PackageManager.INSTALL_FAILED_SESSION_INVALID,
- "Incremental installation of this package is not allowed.");
+ if (isIncrementalInstallation()) {
+ if (!isIncrementalInstallationAllowed(mPackageName)) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_SESSION_INVALID,
+ "Incremental installation of this package is not allowed.");
+ }
+ // Since we moved the staged app metadata file so that incfs can be initialized, lets
+ // now move it back.
+ File appMetadataFile = getTmpAppMetadataFile();
+ if (appMetadataFile.exists()) {
+ final IncrementalFileStorages incrementalFileStorages =
+ getIncrementalFileStorages();
+ try {
+ incrementalFileStorages.makeFile(APP_METADATA_FILE_NAME,
+ Files.readAllBytes(appMetadataFile.toPath()));
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write app metadata to incremental storage", e);
+ } finally {
+ appMetadataFile.delete();
+ }
+ }
}
if (mInstallerUid != mOriginalInstallerUid) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 759ec67cfba8..edc6b4a085f6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15,6 +15,7 @@
package com.android.server.pm;
+import static android.Manifest.permission.GET_APP_METADATA;
import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.app.AppOpsManager.MODE_IGNORED;
@@ -559,6 +560,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
static final String RANDOM_DIR_PREFIX = "~~";
static final char RANDOM_CODEPATH_PREFIX = '-';
+ static final String APP_METADATA_FILE_NAME = "app.metadata";
+
final Handler mHandler;
final Handler mBackgroundHandler;
@@ -5058,6 +5061,20 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@Override
+ public String getAppMetadataPath(String packageName, int userId) {
+ mContext.enforceCallingOrSelfPermission(GET_APP_METADATA, "getAppMetadataPath");
+ final int callingUid = Binder.getCallingUid();
+ final Computer snapshot = snapshotComputer();
+ final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered(
+ packageName, callingUid, userId);
+ if (ps == null) {
+ throw new ParcelableException(
+ new PackageManager.NameNotFoundException(packageName));
+ }
+ return new File(ps.getPathString(), APP_METADATA_FILE_NAME).getAbsolutePath();
+ }
+
+ @Override
public String getPermissionControllerPackageName() {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 365e0ebe2e07..e6600469e998 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2170,9 +2170,9 @@ public class UserManagerService extends IUserManager.Stub {
*/
private @CrossProfileIntentFilter.AccessControlLevel int
getCrossProfileIntentFilterAccessControl(@UserIdInt int userId) {
- final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
- return userTypeDetails != null ? userTypeDetails.getCrossProfileIntentFilterAccessControl()
- : CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
+ final UserProperties userProperties = getUserPropertiesInternal(userId);
+ return userProperties != null ? userProperties.getCrossProfileIntentFilterAccessControl() :
+ CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
}
/**
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index ebb9f98320cf..ddf3692f8d85 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -165,14 +165,6 @@ public final class UserTypeDetails {
private final boolean mIsCredentialSharableWithParent;
/**
- * Denotes the default access control for {@link CrossProfileIntentFilter} of user profile.
- *
- * <p> Default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
- */
- private final @CrossProfileIntentFilter.AccessControlLevel int
- mCrossProfileIntentFilterAccessControl;
-
- /**
* The default {@link UserProperties} for the user type.
* <p> The uninitialized value of each property is implied by {@link UserProperties.Builder}.
*/
@@ -190,7 +182,6 @@ public final class UserTypeDetails {
@Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
boolean isMediaSharedWithParent,
boolean isCredentialSharableWithParent,
- @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel,
@NonNull UserProperties defaultUserProperties) {
this.mName = name;
this.mEnabled = enabled;
@@ -212,7 +203,6 @@ public final class UserTypeDetails {
this.mDarkThemeBadgeColors = darkThemeBadgeColors;
this.mIsMediaSharedWithParent = isMediaSharedWithParent;
this.mIsCredentialSharableWithParent = isCredentialSharableWithParent;
- this.mCrossProfileIntentFilterAccessControl = accessControlLevel;
this.mDefaultUserProperties = defaultUserProperties;
}
@@ -334,15 +324,6 @@ public final class UserTypeDetails {
return mIsCredentialSharableWithParent;
}
- /**
- * Returning user's {@link CrossProfileIntentFilter.AccessControlLevel}. If not explicitly
- * configured, default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
- * @return user's {@link CrossProfileIntentFilter.AccessControlLevel}
- */
- public @CrossProfileIntentFilter.AccessControlLevel int
- getCrossProfileIntentFilterAccessControl() {
- return mCrossProfileIntentFilterAccessControl;
- }
/**
* Returns the reference to the default {@link UserProperties} for this type of user.
@@ -458,8 +439,6 @@ public final class UserTypeDetails {
private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
private boolean mIsMediaSharedWithParent = false;
private boolean mIsCredentialSharableWithParent = false;
- private @CrossProfileIntentFilter.AccessControlLevel int
- mCrossProfileIntentFilterAccessControl = CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
// Default UserProperties cannot be null but for efficiency we don't initialize it now.
// If it isn't set explicitly, {@link UserProperties.Builder#build()} will be used.
private @Nullable UserProperties mDefaultUserProperties = null;
@@ -563,16 +542,6 @@ public final class UserTypeDetails {
}
/**
- * Sets {@link CrossProfileIntentFilter.AccessControlLevel} for the user.
- * @param accessControlLevel default access control for user
- */
- public Builder setCrossProfileIntentFilterAccessControl(
- @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) {
- mCrossProfileIntentFilterAccessControl = accessControlLevel;
- return this;
- }
-
- /**
* Sets shared media property for the user.
* @param isCredentialSharableWithParent the value to be set, true or false
*/
@@ -642,7 +611,6 @@ public final class UserTypeDetails {
mDefaultCrossProfileIntentFilters,
mIsMediaSharedWithParent,
mIsCredentialSharableWithParent,
- mCrossProfileIntentFilterAccessControl,
getDefaultUserProperties());
}
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 2bb72b8cae42..9b337adf8223 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -123,8 +123,6 @@ public final class UserTypeFactory {
.setLabel(0)
.setDefaultRestrictions(null)
.setIsMediaSharedWithParent(true)
- .setCrossProfileIntentFilterAccessControl(
- CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM)
.setIsCredentialSharableWithParent(true)
.setDefaultCrossProfileIntentFilters(getDefaultCloneCrossProfileIntentFilter())
.setDefaultUserProperties(new UserProperties.Builder()
@@ -133,7 +131,9 @@ public final class UserTypeFactory {
.setShowInSettings(UserProperties.SHOW_IN_SETTINGS_WITH_PARENT)
.setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
.setUseParentsContacts(true)
- .setUpdateCrossProfileIntentFiltersOnOTA(true));
+ .setUpdateCrossProfileIntentFiltersOnOTA(true)
+ .setCrossProfileIntentFilterAccessControl(
+ UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM));
}
/**
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index 5cbe74c5986c..a0e91ad7cf45 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -1,4 +1,5 @@
michaelwr@google.com
santoscordon@google.com
+philipjunker@google.com
per-file ThermalManagerService.java=wvw@google.com
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 721872b73f6a..83a628257564 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -563,7 +563,12 @@ public class TrustManagerService extends SystemService {
changed = mUserIsTrusted.get(userId) != trusted;
mUserIsTrusted.put(userId, trusted);
}
- dispatchOnTrustChanged(trusted, userId, flags, getTrustGrantedMessages(userId));
+ dispatchOnTrustChanged(
+ trusted,
+ false /* newlyUnlocked */,
+ userId,
+ flags,
+ getTrustGrantedMessages(userId));
if (changed) {
refreshDeviceLockedForUser(userId);
if (!trusted) {
@@ -628,7 +633,9 @@ public class TrustManagerService extends SystemService {
if (DEBUG) Slog.d(TAG, "pendingTrustState: " + pendingTrustState);
boolean isNowTrusted = pendingTrustState == TrustState.TRUSTED;
- dispatchOnTrustChanged(isNowTrusted, userId, flags, getTrustGrantedMessages(userId));
+ boolean newlyUnlocked = !alreadyUnlocked && isNowTrusted;
+ dispatchOnTrustChanged(
+ isNowTrusted, newlyUnlocked, userId, flags, getTrustGrantedMessages(userId));
if (isNowTrusted != wasTrusted) {
refreshDeviceLockedForUser(userId);
if (!isNowTrusted) {
@@ -643,8 +650,7 @@ public class TrustManagerService extends SystemService {
}
}
- boolean wasLocked = !alreadyUnlocked;
- boolean shouldSendCallback = wasLocked && pendingTrustState == TrustState.TRUSTED;
+ boolean shouldSendCallback = newlyUnlocked;
if (shouldSendCallback) {
if (resultCallback != null) {
if (DEBUG) Slog.d(TAG, "calling back with UNLOCKED_BY_GRANT");
@@ -1387,16 +1393,17 @@ public class TrustManagerService extends SystemService {
}
}
- private void dispatchOnTrustChanged(boolean enabled, int userId, int flags,
- @NonNull List<String> trustGrantedMessages) {
+ private void dispatchOnTrustChanged(boolean enabled, boolean newlyUnlocked, int userId,
+ int flags, @NonNull List<String> trustGrantedMessages) {
if (DEBUG) {
- Log.i(TAG, "onTrustChanged(" + enabled + ", " + userId + ", 0x"
+ Log.i(TAG, "onTrustChanged(" + enabled + ", " + newlyUnlocked + ", " + userId + ", 0x"
+ Integer.toHexString(flags) + ")");
}
if (!enabled) flags = 0;
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
- mTrustListeners.get(i).onTrustChanged(enabled, userId, flags, trustGrantedMessages);
+ mTrustListeners.get(i).onTrustChanged(
+ enabled, newlyUnlocked, userId, flags, trustGrantedMessages);
} catch (DeadObjectException e) {
Slog.d(TAG, "Removing dead TrustListener.");
mTrustListeners.remove(i);
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 3be16a1fec44..739aff7e87c8 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -33,6 +33,7 @@ import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
import static com.android.server.VcnManagementService.LOCAL_LOG;
import static com.android.server.VcnManagementService.VDBG;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -59,6 +60,7 @@ import android.net.RouteInfo;
import android.net.TelephonyNetworkSpecifier;
import android.net.Uri;
import android.net.annotations.PolicyDirection;
+import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
import android.net.ipsec.ike.ChildSessionParams;
@@ -67,11 +69,14 @@ import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnManager;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.os.Handler;
@@ -169,6 +174,9 @@ import java.util.function.Consumer;
public class VcnGatewayConnection extends StateMachine {
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
+ /** Default number of parallel SAs requested */
+ static final int TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT = 1;
+
// Matches DataConnection.NETWORK_TYPE private constant, and magic string from
// ConnectivityManager#getNetworkTypeName()
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -1980,6 +1988,22 @@ public class VcnGatewayConnection extends StateMachine {
mChildConfig,
oldChildConfig,
mIkeConnectionInfo);
+
+ // Create opportunistic child SAs; this allows SA aggregation in the downlink,
+ // reducing lock/atomic contention in high throughput scenarios. All SAs will
+ // share the same UDP encap socket (and keepalives) as necessary, and are
+ // effectively free.
+ final int parallelTunnelCount =
+ mDeps.getParallelTunnelCount(mLastSnapshot, mSubscriptionGroup);
+ logInfo("Parallel tunnel count: " + parallelTunnelCount);
+
+ for (int i = 0; i < parallelTunnelCount - 1; i++) {
+ mIkeSession.openChildSession(
+ buildOpportunisticChildParams(),
+ new VcnChildSessionCallback(
+ mCurrentToken, true /* isOpportunistic */));
+ }
+
break;
case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
@@ -2350,15 +2374,44 @@ public class VcnGatewayConnection extends StateMachine {
@VisibleForTesting(visibility = Visibility.PRIVATE)
public class VcnChildSessionCallback implements ChildSessionCallback {
private final int mToken;
+ private final boolean mIsOpportunistic;
+
+ private boolean mIsChildOpened = false;
VcnChildSessionCallback(int token) {
+ this(token, false /* isOpportunistic */);
+ }
+
+ /**
+ * Creates a ChildSessionCallback
+ *
+ * <p>If configured as opportunistic, transforms will not report initial startup, or
+ * associated startup failures. This serves the dual purposes of ensuring that if the server
+ * does not support connection multiplexing, new child SA negotiations will be ignored, and
+ * at the same time, will notify the VCN session if a successfully negotiated opportunistic
+ * child SA is subsequently torn down, which could impact uplink traffic if the SA in use
+ * for outbound/uplink traffic is this opportunistic SA.
+ *
+ * <p>While inbound SAs can be used in parallel, the IPsec stack explicitly selects the last
+ * applied outbound transform for outbound traffic. This means that unlike inbound traffic,
+ * outbound does not benefit from these parallel SAs in the same manner.
+ */
+ VcnChildSessionCallback(int token, boolean isOpportunistic) {
mToken = token;
+ mIsOpportunistic = isOpportunistic;
}
/** Internal proxy method for injecting of mocked ChildSessionConfiguration */
@VisibleForTesting(visibility = Visibility.PRIVATE)
void onOpened(@NonNull VcnChildSessionConfiguration childConfig) {
logDbg("ChildOpened for token " + mToken);
+
+ if (mIsOpportunistic) {
+ logDbg("ChildOpened for opportunistic child; suppressing event message");
+ mIsChildOpened = true;
+ return;
+ }
+
childOpened(mToken, childConfig);
}
@@ -2370,12 +2423,24 @@ public class VcnGatewayConnection extends StateMachine {
@Override
public void onClosed() {
logDbg("ChildClosed for token " + mToken);
+
+ if (mIsOpportunistic && !mIsChildOpened) {
+ logDbg("ChildClosed for unopened opportunistic child; ignoring");
+ return;
+ }
+
sessionLost(mToken, null);
}
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
logInfo("ChildClosedExceptionally for token " + mToken, exception);
+
+ if (mIsOpportunistic && !mIsChildOpened) {
+ logInfo("ChildClosedExceptionally for unopened opportunistic child; ignoring");
+ return;
+ }
+
sessionLost(mToken, exception);
}
@@ -2580,6 +2645,30 @@ public class VcnGatewayConnection extends StateMachine {
return mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams();
}
+ private ChildSessionParams buildOpportunisticChildParams() {
+ final ChildSessionParams baseParams =
+ mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams();
+
+ final TunnelModeChildSessionParams.Builder builder =
+ new TunnelModeChildSessionParams.Builder();
+ for (ChildSaProposal proposal : baseParams.getChildSaProposals()) {
+ builder.addChildSaProposal(proposal);
+ }
+
+ for (IkeTrafficSelector inboundSelector : baseParams.getInboundTrafficSelectors()) {
+ builder.addInboundTrafficSelectors(inboundSelector);
+ }
+
+ for (IkeTrafficSelector outboundSelector : baseParams.getOutboundTrafficSelectors()) {
+ builder.addOutboundTrafficSelectors(outboundSelector);
+ }
+
+ builder.setLifetimeSeconds(
+ baseParams.getHardLifetimeSeconds(), baseParams.getSoftLifetimeSeconds());
+
+ return builder.build();
+ }
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnIkeSession buildIkeSession(@NonNull Network network) {
final int token = ++mCurrentToken;
@@ -2680,6 +2769,23 @@ public class VcnGatewayConnection extends StateMachine {
return 0;
}
}
+
+ /** Gets the max number of parallel tunnels allowed for tunnel aggregation. */
+ public int getParallelTunnelCount(
+ TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) {
+ PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp);
+ int result = TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT;
+
+ if (carrierConfig != null) {
+ result =
+ carrierConfig.getInt(
+ VcnManager.VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY,
+ TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT);
+ }
+
+ // Guard against tunnel count < 1
+ return Math.max(1, result);
+ }
}
/**
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 2f84fddc7278..2141eba3be50 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -15,6 +15,7 @@
*/
package com.android.server.vcn.routeselection;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -45,6 +46,7 @@ import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscription
import com.android.server.vcn.VcnContext;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -69,9 +71,23 @@ class NetworkPriorityClassifier {
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
- /** Priority for any other networks (including unvalidated, etc) */
+ /**
+ * Priority for networks that VCN can fall back to.
+ *
+ * <p>If none of the network candidates are validated or match any template, VCN will fall back
+ * to any INTERNET network.
+ */
@VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_ANY = Integer.MAX_VALUE;
+ static final int PRIORITY_FALLBACK = Integer.MAX_VALUE;
+
+ /**
+ * Priority for networks that cannot be selected as VCN's underlying networks.
+ *
+ * <p>VCN MUST never select a non-INTERNET network that are unvalidated or fail to match any
+ * template as the underlying network.
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_INVALID = -1;
/** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */
public static int calculatePriorityClass(
@@ -86,12 +102,12 @@ class NetworkPriorityClassifier {
if (networkRecord.isBlocked) {
logWtf("Network blocked for System Server: " + networkRecord.network);
- return PRIORITY_ANY;
+ return PRIORITY_INVALID;
}
if (snapshot == null) {
logWtf("Got null snapshot");
- return PRIORITY_ANY;
+ return PRIORITY_INVALID;
}
int priorityIndex = 0;
@@ -108,7 +124,13 @@ class NetworkPriorityClassifier {
}
priorityIndex++;
}
- return PRIORITY_ANY;
+
+ final NetworkCapabilities caps = networkRecord.networkCapabilities;
+ if (caps.hasCapability(NET_CAPABILITY_INTERNET)
+ || (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST))) {
+ return PRIORITY_FALLBACK;
+ }
+ return PRIORITY_INVALID;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -297,6 +319,18 @@ class NetworkPriorityClassifier {
return false;
}
+ for (Map.Entry<Integer, Integer> entry :
+ networkPriority.getCapabilitiesMatchCriteria().entrySet()) {
+ final int cap = entry.getKey();
+ final int matchCriteria = entry.getValue();
+
+ if (matchCriteria == MATCH_REQUIRED && !caps.hasCapability(cap)) {
+ return false;
+ } else if (matchCriteria == MATCH_FORBIDDEN && caps.hasCapability(cap)) {
+ return false;
+ }
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index d474c5d33a93..6afa795e96fa 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -16,6 +16,9 @@
package com.android.server.vcn.routeselection;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
import static com.android.server.VcnManagementService.LOCAL_LOG;
@@ -32,6 +35,7 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
@@ -40,6 +44,7 @@ import android.os.ParcelUuid;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -49,6 +54,7 @@ import com.android.server.vcn.VcnContext;
import com.android.server.vcn.util.LogUtils;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -126,6 +132,63 @@ public class UnderlyingNetworkController {
registerOrUpdateNetworkRequests();
}
+ private static class CapabilityMatchCriteria {
+ public final int capability;
+ public final int matchCriteria;
+
+ CapabilityMatchCriteria(int capability, int matchCriteria) {
+ this.capability = capability;
+ this.matchCriteria = matchCriteria;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(capability, matchCriteria);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof CapabilityMatchCriteria)) {
+ return false;
+ }
+
+ final CapabilityMatchCriteria rhs = (CapabilityMatchCriteria) other;
+ return capability == rhs.capability && matchCriteria == rhs.matchCriteria;
+ }
+ }
+
+ private static Set<Set<CapabilityMatchCriteria>> dedupAndGetCapRequirementsForCell(
+ VcnGatewayConnectionConfig connectionConfig) {
+ final Set<Set<CapabilityMatchCriteria>> dedupedCapsMatchSets = new ArraySet<>();
+
+ for (VcnUnderlyingNetworkTemplate template :
+ connectionConfig.getVcnUnderlyingNetworkPriorities()) {
+ if (template instanceof VcnCellUnderlyingNetworkTemplate) {
+ final Set<CapabilityMatchCriteria> capsMatchSet = new ArraySet<>();
+
+ for (Map.Entry<Integer, Integer> entry :
+ ((VcnCellUnderlyingNetworkTemplate) template)
+ .getCapabilitiesMatchCriteria()
+ .entrySet()) {
+
+ final int capability = entry.getKey();
+ final int matchCriteria = entry.getValue();
+ if (matchCriteria != MATCH_ANY) {
+ capsMatchSet.add(new CapabilityMatchCriteria(capability, matchCriteria));
+ }
+ }
+
+ dedupedCapsMatchSets.add(capsMatchSet);
+ }
+ }
+
+ dedupedCapsMatchSets.add(
+ Collections.singleton(
+ new CapabilityMatchCriteria(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET, MATCH_REQUIRED)));
+ return dedupedCapsMatchSets;
+ }
+
private void registerOrUpdateNetworkRequests() {
NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback;
NetworkCallback oldWifiCallback = mWifiBringupCallback;
@@ -158,11 +221,14 @@ public class UnderlyingNetworkController {
getWifiNetworkRequest(), mWifiBringupCallback, mHandler);
for (final int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
- final NetworkBringupCallback cb = new NetworkBringupCallback();
- mCellBringupCallbacks.add(cb);
+ for (Set<CapabilityMatchCriteria> capsMatchCriteria :
+ dedupAndGetCapRequirementsForCell(mConnectionConfig)) {
+ final NetworkBringupCallback cb = new NetworkBringupCallback();
+ mCellBringupCallbacks.add(cb);
- mConnectivityManager.requestBackgroundNetwork(
- getCellNetworkRequestForSubId(subId), cb, mHandler);
+ mConnectivityManager.requestBackgroundNetwork(
+ getCellNetworkRequestForSubId(subId, capsMatchCriteria), cb, mHandler);
+ }
}
} else {
mRouteSelectionCallback = null;
@@ -214,6 +280,13 @@ public class UnderlyingNetworkController {
.build();
}
+ private NetworkRequest.Builder getBaseWifiNetworkRequestBuilder() {
+ return getBaseNetworkRequestBuilder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
+ }
+
/**
* Builds the WiFi bringup request
*
@@ -224,10 +297,7 @@ public class UnderlyingNetworkController {
* but will NEVER bring up a Carrier WiFi network itself.
*/
private NetworkRequest getWifiNetworkRequest() {
- return getBaseNetworkRequestBuilder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
- .build();
+ return getBaseWifiNetworkRequestBuilder().build();
}
/**
@@ -238,9 +308,7 @@ public class UnderlyingNetworkController {
* pace to effectively select a short-lived WiFi offload network.
*/
private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() {
- return getBaseNetworkRequestBuilder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+ return getBaseWifiNetworkRequestBuilder()
// Ensure wifi updates signal strengths when crossing this threshold.
.setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig))
.build();
@@ -254,9 +322,7 @@ public class UnderlyingNetworkController {
* pace to effectively select away from a failing WiFi network.
*/
private NetworkRequest getWifiExitRssiThresholdNetworkRequest() {
- return getBaseNetworkRequestBuilder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+ return getBaseWifiNetworkRequestBuilder()
// Ensure wifi updates signal strengths when crossing this threshold.
.setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig))
.build();
@@ -273,11 +339,25 @@ public class UnderlyingNetworkController {
* <p>Since this request MUST make it to the TelephonyNetworkFactory, subIds are not specified
* in the NetworkCapabilities, but rather in the TelephonyNetworkSpecifier.
*/
- private NetworkRequest getCellNetworkRequestForSubId(int subId) {
- return getBaseNetworkRequestBuilder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
- .build();
+ private NetworkRequest getCellNetworkRequestForSubId(
+ int subId, Set<CapabilityMatchCriteria> capsMatchCriteria) {
+ final NetworkRequest.Builder nrBuilder =
+ getBaseNetworkRequestBuilder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+
+ for (CapabilityMatchCriteria capMatchCriteria : capsMatchCriteria) {
+ final int cap = capMatchCriteria.capability;
+ final int matchCriteria = capMatchCriteria.matchCriteria;
+
+ if (matchCriteria == MATCH_REQUIRED) {
+ nrBuilder.addCapability(cap);
+ } else if (matchCriteria == MATCH_FORBIDDEN) {
+ nrBuilder.addForbiddenCapability(cap);
+ }
+ }
+
+ return nrBuilder.build();
}
/**
@@ -285,7 +365,6 @@ public class UnderlyingNetworkController {
*/
private NetworkRequest.Builder getBaseNetworkRequestBuilder() {
return new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
@@ -356,7 +435,7 @@ public class UnderlyingNetworkController {
if (!allNetworkPriorities.isEmpty()) {
allNetworkPriorities += ", ";
}
- allNetworkPriorities += record.network + ": " + record.getPriorityClass();
+ allNetworkPriorities += record.network + ": " + record.priorityClass;
}
logInfo(
"Selected network changed to "
@@ -393,19 +472,22 @@ public class UnderlyingNetworkController {
private TreeSet<UnderlyingNetworkRecord> getSortedUnderlyingNetworks() {
TreeSet<UnderlyingNetworkRecord> sorted =
- new TreeSet<>(
- UnderlyingNetworkRecord.getComparator(
+ new TreeSet<>(UnderlyingNetworkRecord.getComparator());
+
+ for (UnderlyingNetworkRecord.Builder builder :
+ mUnderlyingNetworkRecordBuilders.values()) {
+ if (builder.isValid()) {
+ final UnderlyingNetworkRecord record =
+ builder.build(
mVcnContext,
mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
mSubscriptionGroup,
mLastSnapshot,
mCurrentRecord,
- mCarrierConfig));
-
- for (UnderlyingNetworkRecord.Builder builder :
- mUnderlyingNetworkRecordBuilders.values()) {
- if (builder.isValid()) {
- sorted.add(builder.build());
+ mCarrierConfig);
+ if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) {
+ sorted.add(record);
+ }
}
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index 319680e0b01c..aea9f4d2dbae 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -42,53 +42,58 @@ import java.util.Objects;
* @hide
*/
public class UnderlyingNetworkRecord {
- private static final int PRIORITY_CLASS_INVALID = Integer.MAX_VALUE;
-
@NonNull public final Network network;
@NonNull public final NetworkCapabilities networkCapabilities;
@NonNull public final LinkProperties linkProperties;
public final boolean isBlocked;
-
- private int mPriorityClass = PRIORITY_CLASS_INVALID;
+ public final boolean isSelected;
+ public final int priorityClass;
@VisibleForTesting(visibility = Visibility.PRIVATE)
public UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
- boolean isBlocked) {
- this.network = network;
- this.networkCapabilities = networkCapabilities;
- this.linkProperties = linkProperties;
- this.isBlocked = isBlocked;
- }
-
- private int getOrCalculatePriorityClass(
+ boolean isBlocked,
VcnContext vcnContext,
List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
PersistableBundleWrapper carrierConfig) {
- // Never changes after the underlying network record is created.
- if (mPriorityClass == PRIORITY_CLASS_INVALID) {
- mPriorityClass =
- NetworkPriorityClassifier.calculatePriorityClass(
- vcnContext,
- this,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
- }
+ this.network = network;
+ this.networkCapabilities = networkCapabilities;
+ this.linkProperties = linkProperties;
+ this.isBlocked = isBlocked;
- return mPriorityClass;
+ this.isSelected = isSelected(this.network, currentlySelected);
+
+ priorityClass =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ vcnContext,
+ this,
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ snapshot,
+ currentlySelected,
+ carrierConfig);
}
- // Used in UnderlyingNetworkController
- int getPriorityClass() {
- return mPriorityClass;
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public UnderlyingNetworkRecord(
+ @NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties,
+ boolean isBlocked,
+ boolean isSelected,
+ int priorityClass) {
+ this.network = network;
+ this.networkCapabilities = networkCapabilities;
+ this.linkProperties = linkProperties;
+ this.isBlocked = isBlocked;
+ this.isSelected = isSelected;
+
+ this.priorityClass = priorityClass;
}
@Override
@@ -108,40 +113,32 @@ public class UnderlyingNetworkRecord {
return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
}
- static Comparator<UnderlyingNetworkRecord> getComparator(
- VcnContext vcnContext,
- List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundleWrapper carrierConfig) {
+ /** Returns if two records are equal including their priority classes. */
+ public static boolean isEqualIncludingPriorities(
+ UnderlyingNetworkRecord left, UnderlyingNetworkRecord right) {
+ if (left != null && right != null) {
+ return left.equals(right)
+ && left.isSelected == right.isSelected
+ && left.priorityClass == right.priorityClass;
+ }
+
+ return left == right;
+ }
+
+ static Comparator<UnderlyingNetworkRecord> getComparator() {
return (left, right) -> {
- final int leftIndex =
- left.getOrCalculatePriorityClass(
- vcnContext,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
- final int rightIndex =
- right.getOrCalculatePriorityClass(
- vcnContext,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
+ final int leftIndex = left.priorityClass;
+ final int rightIndex = right.priorityClass;
// In the case of networks in the same priority class, prioritize based on other
// criteria (eg. actively selected network, link metrics, etc)
if (leftIndex == rightIndex) {
// TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord
// fall into the same priority class.
- if (isSelected(left, currentlySelected)) {
+ if (left.isSelected) {
return -1;
}
- if (isSelected(left, currentlySelected)) {
+ if (right.isSelected) {
return 1;
}
}
@@ -150,11 +147,11 @@ public class UnderlyingNetworkRecord {
}
private static boolean isSelected(
- UnderlyingNetworkRecord recordToCheck, UnderlyingNetworkRecord currentlySelected) {
+ Network networkToCheck, UnderlyingNetworkRecord currentlySelected) {
if (currentlySelected == null) {
return false;
}
- if (currentlySelected.network == recordToCheck.network) {
+ if (currentlySelected.network.equals(networkToCheck)) {
return true;
}
return false;
@@ -172,16 +169,8 @@ public class UnderlyingNetworkRecord {
pw.println("UnderlyingNetworkRecord:");
pw.increaseIndent();
- final int priorityIndex =
- getOrCalculatePriorityClass(
- vcnContext,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
-
- pw.println("Priority index: " + priorityIndex);
+ pw.println("priorityClass: " + priorityClass);
+ pw.println("isSelected: " + isSelected);
pw.println("mNetwork: " + network);
pw.println("mNetworkCapabilities: " + networkCapabilities);
pw.println("mLinkProperties: " + linkProperties);
@@ -198,8 +187,6 @@ public class UnderlyingNetworkRecord {
boolean mIsBlocked;
boolean mWasIsBlockedSet;
- @Nullable private UnderlyingNetworkRecord mCached;
-
Builder(@NonNull Network network) {
mNetwork = network;
}
@@ -211,7 +198,6 @@ public class UnderlyingNetworkRecord {
void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
mNetworkCapabilities = networkCapabilities;
- mCached = null;
}
@Nullable
@@ -221,32 +207,40 @@ public class UnderlyingNetworkRecord {
void setLinkProperties(@NonNull LinkProperties linkProperties) {
mLinkProperties = linkProperties;
- mCached = null;
}
void setIsBlocked(boolean isBlocked) {
mIsBlocked = isBlocked;
mWasIsBlockedSet = true;
- mCached = null;
}
boolean isValid() {
return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
}
- UnderlyingNetworkRecord build() {
+ UnderlyingNetworkRecord build(
+ VcnContext vcnContext,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundleWrapper carrierConfig) {
if (!isValid()) {
throw new IllegalArgumentException(
"Called build before UnderlyingNetworkRecord was valid");
}
- if (mCached == null) {
- mCached =
- new UnderlyingNetworkRecord(
- mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
- }
-
- return mCached;
+ return new UnderlyingNetworkRecord(
+ mNetwork,
+ mNetworkCapabilities,
+ mLinkProperties,
+ mIsBlocked,
+ vcnContext,
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ snapshot,
+ currentlySelected,
+ carrierConfig);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
index d22ec0ad456d..d6761a2b37d8 100644
--- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
+++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
@@ -573,5 +573,10 @@ public class PersistableBundleUtils {
return isEqual(mBundle, other.mBundle);
}
+
+ @Override
+ public String toString() {
+ return mBundle.toString();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 2b49a81b34bc..f683d0a6666f 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -67,9 +67,11 @@ import android.app.ICompatCameraControlCallback;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
+import android.app.compat.CompatChanges;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
import android.app.servertransaction.PipStateTransactionItem;
+import android.compat.annotation.ChangeId;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -122,6 +124,19 @@ class ActivityClientController extends IActivityClientController.Stub {
/** Wrapper around VoiceInteractionServiceManager. */
private AssistUtils mAssistUtils;
+ /**
+ * Grants access to the launching app's identity if the app opted-in to sharing its identity
+ * by launching this activity with an instance of {@link android.app.ActivityOptions} on which
+ * {@link android.app.ActivityOptions#setShareIdentityEnabled(boolean)} was invoked with a
+ * value of {@code true}, or if the launched activity's uid is the same as the launching
+ * app's. When this change is enabled and one of these requirements is met, the activity
+ * can access the launching app's uid and package name with {@link
+ * android.app.Activity#getLaunchedFromUid()} and {@link
+ * android.app.Activity#getLaunchedFromPackage()}, respectively.
+ */
+ @ChangeId
+ public static final long ACCESS_SHARED_IDENTITY = 259743961L;
+
ActivityClientController(ActivityTaskManagerService service) {
mService = service;
mGlobalLock = service.mGlobalLock;
@@ -691,7 +706,7 @@ class ActivityClientController extends IActivityClientController.Stub {
final boolean isInternalCaller = isInternalCallerGetLaunchedFrom(uid);
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null && (isInternalCaller || r.mShareIdentity || r.launchedFromUid == uid)) {
+ if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r))) {
return r.launchedFromUid;
}
}
@@ -704,7 +719,7 @@ class ActivityClientController extends IActivityClientController.Stub {
final boolean isInternalCaller = isInternalCallerGetLaunchedFrom(uid);
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null && (isInternalCaller || r.mShareIdentity || r.launchedFromUid == uid)) {
+ if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r))) {
return r.launchedFromPackage;
}
}
@@ -729,6 +744,18 @@ class ActivityClientController extends IActivityClientController.Stub {
return installerNames.length > 0 && callingPkg.getPackageName().equals(installerNames[0]);
}
+ /**
+ * Returns whether the specified {@code uid} can access the launching app's identity by
+ * verifying whether the provided {@code ActivityRecord r} has opted in to sharing its
+ * identity or if the uid of the activity matches that of the launching app.
+ */
+ private static boolean canGetLaunchedFromLocked(int uid, ActivityRecord r) {
+ if (CompatChanges.isChangeEnabled(ACCESS_SHARED_IDENTITY, uid)) {
+ return r.mShareIdentity || r.launchedFromUid == uid;
+ }
+ return false;
+ }
+
@Override
public void setRequestedOrientation(IBinder token, int requestedOrientation) {
final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d85e510932ed..064f4ee91554 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7735,10 +7735,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// configuration. This is important to cases where activities with incompatible
// orientations launch, or user goes back from an activity of bi-orientation to an
// activity with specified orientation.
- if (getRequestedOrientation() == SCREEN_ORIENTATION_UNSET) {
- return;
- }
-
if (onDescendantOrientationChanged(this)) {
// WM Shell can show additional UI elements, e.g. a restart button for size compat mode
// so ensure that WM Shell is called when an activity becomes visible.
@@ -8311,11 +8307,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
- boolean handlesOrientationChangeFromDescendant() {
+ boolean handlesOrientationChangeFromDescendant(int orientation) {
if (shouldIgnoreOrientationRequests()) {
return false;
}
- return super.handlesOrientationChangeFromDescendant();
+ return super.handlesOrientationChangeFromDescendant(orientation);
}
/**
@@ -8337,7 +8333,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If orientation is respected when insets are applied, then stableBounds will be empty.
boolean orientationRespectedWithInsets =
orientationRespectedWithInsets(parentBounds, stableBounds);
- if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) {
+ if (orientationRespectedWithInsets
+ && handlesOrientationChangeFromDescendant(mOrientation)) {
// No need to letterbox because of fixed orientation. Display will handle
// fixed-orientation requests and a display rotation is enough to respect requested
// orientation with insets applied.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 2762c459fba4..6c49f47e6880 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1829,8 +1829,8 @@ class ActivityStarter {
final int launchingFromDisplayId =
mSourceRecord != null ? mSourceRecord.getDisplayId() : DEFAULT_DISPLAY;
if (!displayContent.mDwpcHelper
- .canActivityBeLaunched(r.info, targetWindowingMode, launchingFromDisplayId,
- newTask)) {
+ .canActivityBeLaunched(r.info, r.intent, targetWindowingMode,
+ launchingFromDisplayId, newTask)) {
Slog.w(TAG, "Abort to launch " + r.info.getComponentName()
+ " on display area " + mPreferredTaskDisplayArea);
return START_ABORTED;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index eb04687d7d0c..937f2f1c1a2e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -37,6 +37,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -1260,6 +1261,17 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");
+
+ boolean isSandboxedActivity = (intent != null && intent.getAction() != null
+ && intent.getAction().equals(ACTION_START_SANDBOXED_ACTIVITY));
+ if (isSandboxedActivity) {
+ SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
+ SdkSandboxManagerLocal.class);
+ sdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
+ intent, Binder.getCallingUid(), callingPackage
+ );
+ }
+
if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
SdkSandboxManagerLocal.class);
@@ -4829,6 +4841,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// If there is no resumed activity, it will choose the pausing or focused activity.
: mRootWindowContainer.getTopResumedActivity();
mTopApp = top != null ? top.app : null;
+ if (mTopApp == mPreviousProcess) mPreviousProcess = null;
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 89f1bd043556..a15453e924bd 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -35,6 +35,8 @@ import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
@@ -142,26 +144,30 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
@Override
+ @ScreenOrientation
int getOrientation(int candidate) {
- mLastOrientationSource = null;
- if (getIgnoreOrientationRequest()) {
+ final int orientation = super.getOrientation(candidate);
+ if (getIgnoreOrientationRequest(orientation)) {
+ // In all the other case, mLastOrientationSource will be reassigned to a new value
+ mLastOrientationSource = null;
return SCREEN_ORIENTATION_UNSET;
}
-
- return super.getOrientation(candidate);
+ return orientation;
}
@Override
- boolean handlesOrientationChangeFromDescendant() {
- return !getIgnoreOrientationRequest()
- && super.handlesOrientationChangeFromDescendant();
+ boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) {
+ return !getIgnoreOrientationRequest(orientation)
+ && super.handlesOrientationChangeFromDescendant(orientation);
}
@Override
- boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
+ boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) {
// If this is set to ignore the orientation request, we don't propagate descendant
// orientation request.
- return !getIgnoreOrientationRequest()
+ final int orientation = requestingContainer != null
+ ? requestingContainer.mOrientation : SCREEN_ORIENTATION_UNSET;
+ return !getIgnoreOrientationRequest(orientation)
&& super.onDescendantOrientationChanged(requestingContainer);
}
@@ -225,6 +231,23 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
}
+ /**
+ * @return {@value true} if we need to ignore the orientation in input.
+ */
+ // TODO(b/262366204): Rename getIgnoreOrientationRequest to shouldIgnoreOrientationRequest
+ boolean getIgnoreOrientationRequest(@ScreenOrientation int orientation) {
+ // We always respect orientation request for ActivityInfo.SCREEN_ORIENTATION_LOCKED
+ // ActivityInfo.SCREEN_ORIENTATION_NOSENSOR.
+ // Main use case why this is important is Camera apps that rely on those
+ // properties to ensure that they will be able to determine Camera preview
+ // orientation correctly
+ if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+ return false;
+ }
+ return getIgnoreOrientationRequest();
+ }
+
boolean getIgnoreOrientationRequest() {
// Adding an exception for when ignoreOrientationRequest is overridden at runtime for all
// DisplayArea-s. For example, this is needed for the Kids Mode since many Kids apps aren't
@@ -691,11 +714,9 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
@Override
+ @ScreenOrientation
int getOrientation(int candidate) {
mLastOrientationSource = null;
- if (getIgnoreOrientationRequest()) {
- return SCREEN_ORIENTATION_UNSET;
- }
// Find a window requesting orientation.
final WindowState win = getWindow(mGetOrientingWindow);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c6dc24f32837..ffea07cb2bd2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1601,13 +1601,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@Override
- boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
+ boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) {
final Configuration config = updateOrientation(
requestingContainer, false /* forceUpdate */);
// If display rotation class tells us that it doesn't consider app requested orientation,
// this display won't rotate just because of an app changes its requested orientation. Thus
// it indicates that this display chooses not to handle this request.
- final boolean handled = handlesOrientationChangeFromDescendant();
+ final int orientation = requestingContainer != null ? requestingContainer.mOrientation
+ : SCREEN_ORIENTATION_UNSET;
+ final boolean handled = handlesOrientationChangeFromDescendant(orientation);
if (config == null) {
return handled;
}
@@ -1630,8 +1632,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@Override
- boolean handlesOrientationChangeFromDescendant() {
- return !getIgnoreOrientationRequest()
+ boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) {
+ return !getIgnoreOrientationRequest(orientation)
&& !getDisplayRotation().isFixedToUserRotation();
}
@@ -1732,7 +1734,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return ROTATION_UNDEFINED;
}
if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM
- || getIgnoreOrientationRequest()) {
+ || getIgnoreOrientationRequest(r.mOrientation)) {
return ROTATION_UNDEFINED;
}
if (r.mOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
@@ -2740,15 +2742,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@ScreenOrientation
@Override
int getOrientation() {
- mLastOrientationSource = null;
- if (!handlesOrientationChangeFromDescendant()) {
- // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Display id=%d is ignoring all orientation requests, return %d",
- mDisplayId, SCREEN_ORIENTATION_UNSPECIFIED);
- return SCREEN_ORIENTATION_UNSPECIFIED;
- }
-
if (mWmService.mDisplayFrozen) {
if (mWmService.mPolicy.isKeyguardLocked()) {
// Use the last orientation the while the display is frozen with the keyguard
@@ -2764,6 +2757,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
final int orientation = super.getOrientation();
+
+ if (!handlesOrientationChangeFromDescendant(orientation)) {
+ mLastOrientationSource = null;
+ // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Display id=%d is ignoring orientation request for %d, return %d",
+ mDisplayId, orientation, SCREEN_ORIENTATION_UNSPECIFIED);
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
if (orientation == SCREEN_ORIENTATION_UNSET) {
// Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
ProtoLog.v(WM_DEBUG_ORIENTATION,
@@ -3890,18 +3893,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Called when the focused {@link TaskDisplayArea} on this display may have changed. */
void onLastFocusedTaskDisplayAreaChanged(@Nullable TaskDisplayArea taskDisplayArea) {
- // Only record the TaskDisplayArea that handles orientation request.
- if (taskDisplayArea != null && taskDisplayArea.handlesOrientationChangeFromDescendant()) {
- mOrientationRequestingTaskDisplayArea = taskDisplayArea;
- return;
- }
-
- // If the previous TDA no longer handles orientation request, clear it.
- if (mOrientationRequestingTaskDisplayArea != null
- && !mOrientationRequestingTaskDisplayArea
- .handlesOrientationChangeFromDescendant()) {
- mOrientationRequestingTaskDisplayArea = null;
- }
+ mOrientationRequestingTaskDisplayArea = taskDisplayArea;
}
/**
@@ -5104,13 +5096,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@Override
- int getOrientation(int candidate) {
- if (getIgnoreOrientationRequest()) {
- return SCREEN_ORIENTATION_UNSET;
- }
-
+ @ScreenOrientation
+ int getOrientation(@ScreenOrientation int candidate) {
// IME does not participate in orientation.
- return candidate;
+ return getIgnoreOrientationRequest(candidate) ? SCREEN_ORIENTATION_UNSET : candidate;
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
index 72ed1083043b..154fb0cecac3 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -91,8 +92,8 @@ class DisplayWindowPolicyControllerHelper {
* @see DisplayWindowPolicyController#canActivityBeLaunched(ActivityInfo, int, int, boolean)
*/
public boolean canActivityBeLaunched(ActivityInfo activityInfo,
- @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
- boolean isNewTask) {
+ Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
+ int launchingFromDisplayId, boolean isNewTask) {
if (mDisplayWindowPolicyController == null) {
if (activityInfo.requiredDisplayCategory != null) {
Slog.e(TAG,
@@ -104,8 +105,8 @@ class DisplayWindowPolicyControllerHelper {
}
return true;
}
- return mDisplayWindowPolicyController.canActivityBeLaunched(activityInfo, windowingMode,
- launchingFromDisplayId, isNewTask);
+ return mDisplayWindowPolicyController.canActivityBeLaunched(activityInfo, intent,
+ windowingMode, launchingFromDisplayId, isNewTask);
}
/**
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index bd837940dfb2..dde89e9bca2e 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -28,7 +28,7 @@ import java.util.ArrayList;
/** Helper class to ensure activities are in the right visible state for a container. */
class EnsureActivitiesVisibleHelper {
private final TaskFragment mTaskFragment;
- private ActivityRecord mTop;
+ private ActivityRecord mTopRunningActivity;
private ActivityRecord mStarting;
private boolean mAboveTop;
private boolean mContainerShouldBeVisible;
@@ -54,10 +54,10 @@ class EnsureActivitiesVisibleHelper {
void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
boolean notifyClients) {
mStarting = starting;
- mTop = mTaskFragment.topRunningActivity();
+ mTopRunningActivity = mTaskFragment.topRunningActivity();
// If the top activity is not fullscreen, then we need to make sure any activities under it
// are now visible.
- mAboveTop = mTop != null;
+ mAboveTop = mTopRunningActivity != null;
mContainerShouldBeVisible = mTaskFragment.shouldBeVisible(mStarting);
mBehindFullyOccludedContainer = !mContainerShouldBeVisible;
mConfigChanges = configChanges;
@@ -85,18 +85,19 @@ class EnsureActivitiesVisibleHelper {
reset(starting, configChanges, preserveWindows, notifyClients);
if (DEBUG_VISIBILITY) {
- Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
+ Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTopRunningActivity
+ " configChanges=0x" + Integer.toHexString(configChanges));
}
- if (mTop != null && mTaskFragment.asTask() != null) {
+ if (mTopRunningActivity != null && mTaskFragment.asTask() != null) {
// TODO(14709632): Check if this needed to be implemented in TaskFragment.
- mTaskFragment.asTask().checkTranslucentActivityWaiting(mTop);
+ mTaskFragment.asTask().checkTranslucentActivityWaiting(mTopRunningActivity);
}
// We should not resume activities that being launched behind because these
// activities are actually behind other fullscreen activities, but still required
// to be visible (such as performing Recents animation).
- final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
+ final boolean resumeTopActivity = mTopRunningActivity != null
+ && !mTopRunningActivity.mLaunchTaskBehind
&& mTaskFragment.canBeResumed(starting)
&& (starting == null || !starting.isDescendantOf(mTaskFragment));
@@ -113,7 +114,7 @@ class EnsureActivitiesVisibleHelper {
mBehindFullyOccludedContainer |=
(childTaskFragment.getBounds().equals(mTaskFragment.getBounds())
&& !childTaskFragment.isTranslucent(starting));
- if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) {
+ if (mAboveTop && mTopRunningActivity.getTaskFragment() == childTaskFragment) {
mAboveTop = false;
}
@@ -147,8 +148,11 @@ class EnsureActivitiesVisibleHelper {
private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
final boolean resumeTopActivity) {
- final boolean isTop = r == mTop;
+ final boolean isTop = r == mTopRunningActivity;
if (mAboveTop && !isTop) {
+ // Ensure activities above the top-running activity to be invisible because the
+ // activity should be finishing or cannot show to current user.
+ r.makeInvisible();
return;
}
mAboveTop = false;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9367a70cafe3..21f470d829f3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -144,6 +144,7 @@ import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -2674,8 +2675,8 @@ class Task extends TaskFragment {
}
@Override
- boolean handlesOrientationChangeFromDescendant() {
- if (!super.handlesOrientationChangeFromDescendant()) {
+ boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) {
+ if (!super.handlesOrientationChangeFromDescendant(orientation)) {
return false;
}
@@ -2690,7 +2691,7 @@ class Task extends TaskFragment {
// Check for leaf Task.
// Display won't rotate for the orientation request if the Task/TaskDisplayArea
// can't specify orientation.
- return canSpecifyOrientation() && getDisplayArea().canSpecifyOrientation();
+ return canSpecifyOrientation() && getDisplayArea().canSpecifyOrientation(orientation);
}
void resize(boolean relayout, boolean forced) {
@@ -2760,6 +2761,13 @@ class Task extends TaskFragment {
@Override
void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
Rect outSurfaceInsets) {
+ // If this task has its adjacent task, it means they should animate together. Use display
+ // bounds for them could move same as full screen task.
+ if (getAdjacentTaskFragment() != null && getAdjacentTaskFragment().asTask() != null) {
+ super.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
+ return;
+ }
+
final WindowState windowState = getTopVisibleAppMainWindow();
if (windowState != null) {
windowState.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 25b58fa622e7..66b73429225f 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -41,6 +41,7 @@ import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.UserHandle;
@@ -633,22 +634,20 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
@Override
- int getOrientation(int candidate) {
- mLastOrientationSource = null;
- if (getIgnoreOrientationRequest()) {
- return SCREEN_ORIENTATION_UNSET;
- }
- if (!canSpecifyOrientation()) {
+ @ScreenOrientation
+ int getOrientation(@ScreenOrientation int candidate) {
+ final int orientation = super.getOrientation(candidate);
+ if (!canSpecifyOrientation(orientation)) {
+ mLastOrientationSource = null;
// We only respect orientation of the focused TDA, which can be a child of this TDA.
- return reduceOnAllTaskDisplayAreas((taskDisplayArea, orientation) -> {
- if (taskDisplayArea == this || orientation != SCREEN_ORIENTATION_UNSET) {
- return orientation;
+ return reduceOnAllTaskDisplayAreas((taskDisplayArea, taskOrientation) -> {
+ if (taskDisplayArea == this || taskOrientation != SCREEN_ORIENTATION_UNSET) {
+ return taskOrientation;
}
return taskDisplayArea.getOrientation(candidate);
}, SCREEN_ORIENTATION_UNSET);
}
- final int orientation = super.getOrientation(candidate);
if (orientation != SCREEN_ORIENTATION_UNSET
&& orientation != SCREEN_ORIENTATION_BEHIND) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
@@ -1872,12 +1871,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
/** Whether this task display area can request orientation. */
- boolean canSpecifyOrientation() {
- // Only allow to specify orientation if this TDA is not set to ignore orientation request,
- // and it is the last focused one on this logical display that can request orientation
- // request.
- return !getIgnoreOrientationRequest()
- && mDisplayContent.getOrientationRequestingTaskDisplayArea() == this;
+ boolean canSpecifyOrientation(@ScreenOrientation int orientation) {
+ // Only allow to specify orientation if this TDA is the last focused one on this logical
+ // display that can request orientation request.
+ return mDisplayContent.getOrientationRequestingTaskDisplayArea() == this
+ && !getIgnoreOrientationRequest(orientation);
}
void clearPreferredTopFocusableRootTask() {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 0190062eacf7..6219203483f4 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -229,11 +229,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
private TaskFragment mCompanionTaskFragment;
/**
- * Prevents duplicate calls to onTaskAppeared.
+ * Prevents duplicate calls to onTaskFragmentAppeared.
*/
boolean mTaskFragmentAppearedSent;
/**
+ * Prevents unnecessary callbacks after onTaskFragmentVanished.
+ */
+ boolean mTaskFragmentVanishedSent;
+
+ /**
* The last running activity of the TaskFragment was finished due to clear task while launching
* an activity in the Task.
*/
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 6e4df794f8d8..90a0dffa25f2 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -553,6 +553,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) {
+ if (taskFragment.mTaskFragmentVanishedSent) {
+ return;
+ }
if (taskFragment.getTask() == null) {
Slog.w(TAG, "onTaskFragmentAppeared failed because it is not attached tf="
+ taskFragment);
@@ -574,6 +577,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) {
+ if (taskFragment.mTaskFragmentVanishedSent) {
+ return;
+ }
validateAndGetState(organizer);
if (!taskFragment.mTaskFragmentAppearedSent) {
// Skip if TaskFragment still not appeared.
@@ -586,10 +592,6 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setTaskFragment(taskFragment)
.build();
} else {
- if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
- // Skipped the info changed event if vanished event is pending.
- return;
- }
// Remove and add for re-ordering.
removePendingEvent(pendingEvent);
// Reset the defer time when TaskFragment is changed, so that it can check again if
@@ -602,6 +604,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) {
+ if (taskFragment.mTaskFragmentVanishedSent) {
+ return;
+ }
+ taskFragment.mTaskFragmentVanishedSent = true;
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
final List<PendingTaskFragmentEvent> pendingEvents = mPendingTaskFragmentEvents
.get(organizer.asBinder());
@@ -617,20 +623,18 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setTaskFragment(taskFragment)
.build());
state.removeTaskFragment(taskFragment);
+ // Make sure the vanished event will be dispatched if there are no other changes.
+ mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
void onTaskFragmentError(@NonNull ITaskFragmentOrganizer organizer,
@Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
int opType, @NonNull Throwable exception) {
- validateAndGetState(organizer);
- Slog.w(TAG, "onTaskFragmentError ", exception);
- final PendingTaskFragmentEvent vanishedEvent = taskFragment != null
- ? getPendingTaskFragmentEvent(taskFragment, PendingTaskFragmentEvent.EVENT_VANISHED)
- : null;
- if (vanishedEvent != null) {
- // No need to notify if the TaskFragment has been removed.
+ if (taskFragment != null && taskFragment.mTaskFragmentVanishedSent) {
return;
}
+ validateAndGetState(organizer);
+ Slog.w(TAG, "onTaskFragmentError ", exception);
addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_ERROR, organizer)
.setErrorCallbackToken(errorCallbackToken)
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 64574a7e215b..d676c17d2ac7 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -1459,9 +1460,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @return {@code true} if it handles or will handle orientation change in the future; {@code
* false} if it won't handle the change at anytime.
*/
- boolean handlesOrientationChangeFromDescendant() {
+ boolean handlesOrientationChangeFromDescendant(int orientation) {
final WindowContainer parent = getParent();
- return parent != null && parent.handlesOrientationChangeFromDescendant();
+ return parent != null && parent.handlesOrientationChangeFromDescendant(orientation);
}
/**
@@ -1553,7 +1554,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// portrait but the task is still in landscape. While updating from display,
// the task can be updated to portrait first so the configuration can be
// computed in a consistent environment.
- && (inMultiWindowMode() || !handlesOrientationChangeFromDescendant())) {
+ && (inMultiWindowMode()
+ || !handlesOrientationChangeFromDescendant(orientation))) {
// Resolve the requested orientation.
onConfigurationChanged(parent.getConfiguration());
}
@@ -3237,7 +3239,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (isOrganized()
// TODO(b/161711458): Clean-up when moved to shell.
&& getWindowingMode() != WINDOWING_MODE_FULLSCREEN
- && getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ && getWindowingMode() != WINDOWING_MODE_FREEFORM
+ && getWindowingMode() != WINDOWING_MODE_MULTI_WINDOW) {
return null;
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 72411727361a..4ec11538f403 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1059,7 +1059,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (activity == null || activity.finishing) {
break;
}
- if (activity.isVisible()) {
+ if (activity.isVisible() || activity.isVisibleRequested()) {
// Prevent the transition from being executed too early if the activity is
// visible.
activity.finishIfPossible("finish-activity-op", false /* oomAdj */);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index e9b81ec03081..dcd30bb247db 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -65,6 +65,7 @@ import android.os.LocaleList;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -263,7 +264,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
boolean isSysUiPackage = info.packageName.equals(
mAtm.getSysUiServiceComponentLocked().getPackageName());
- if (isSysUiPackage || mUid == Process.SYSTEM_UID) {
+ if (isSysUiPackage || UserHandle.getAppId(mUid) == Process.SYSTEM_UID) {
// This is a system owned process and should not use an activity config.
// TODO(b/151161907): Remove after support for display-independent (raw) SysUi configs.
mIsActivityConfigOverrideAllowed = false;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1b7bd9e1f36f..bc382e0b50af 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -61,6 +61,7 @@ import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NOT_MAGNIFIABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
@@ -1152,12 +1153,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mInputWindowHandle.setName(getName());
mInputWindowHandle.setPackageName(mAttrs.packageName);
mInputWindowHandle.setLayoutParamsType(mAttrs.type);
- // Check private trusted overlay flag and window type to set trustedOverlay variable of
- // input window handle.
- mInputWindowHandle.setTrustedOverlay(
- ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
- && mOwnerCanAddInternalSystemWindow)
- || InputMonitor.isTrustedOverlay(mAttrs.type));
+ mInputWindowHandle.setTrustedOverlay(shouldWindowHandleBeTrusted(s));
if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -1238,6 +1234,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
: service.mAtmService.getProcessController(s.mPid, s.mUid);
}
+ boolean shouldWindowHandleBeTrusted(Session s) {
+ return InputMonitor.isTrustedOverlay(mAttrs.type)
+ || ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
+ && s.mCanAddInternalSystemWindow)
+ || ((mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0
+ && s.mCanCreateSystemApplicationOverlay);
+ }
+
int getTouchOcclusionMode() {
if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) {
return TouchOcclusionMode.USE_OPACITY;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index c36c57159279..d6cf4735e585 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -121,6 +121,7 @@ static struct {
jmethodID getInputPortAssociations;
jmethodID getInputUniqueIdAssociations;
jmethodID getDeviceTypeAssociations;
+ jmethodID getKeyboardLayoutAssociations;
jmethodID getKeyRepeatTimeout;
jmethodID getKeyRepeatDelay;
jmethodID getHoverTapTimeout;
@@ -412,8 +413,10 @@ private:
void ensureSpriteControllerLocked();
sp<SurfaceControl> getParentSurfaceForPointers(int displayId);
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
- std::unordered_map<std::string, std::string> readMapFromInterleavedJavaArray(
- jmethodID method, const char* methodName);
+ template <typename T>
+ std::unordered_map<std::string, T> readMapFromInterleavedJavaArray(
+ jmethodID method, const char* methodName,
+ std::function<T(std::string)> opOnValue = [](auto&& v) { return std::move(v); });
static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); }
};
@@ -588,12 +591,23 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon
}
outConfig->uniqueIdAssociations =
- readMapFromInterleavedJavaArray(gServiceClassInfo.getInputUniqueIdAssociations,
- "getInputUniqueIdAssociations");
+ readMapFromInterleavedJavaArray<std::string>(gServiceClassInfo
+ .getInputUniqueIdAssociations,
+ "getInputUniqueIdAssociations");
outConfig->deviceTypeAssociations =
- readMapFromInterleavedJavaArray(gServiceClassInfo.getDeviceTypeAssociations,
- "getDeviceTypeAssociations");
+ readMapFromInterleavedJavaArray<std::string>(gServiceClassInfo
+ .getDeviceTypeAssociations,
+ "getDeviceTypeAssociations");
+ outConfig->keyboardLayoutAssociations = readMapFromInterleavedJavaArray<
+ KeyboardLayoutInfo>(gServiceClassInfo.getKeyboardLayoutAssociations,
+ "getKeyboardLayoutAssociations", [](auto&& layoutIdentifier) {
+ size_t commaPos = layoutIdentifier.find(',');
+ std::string languageTag = layoutIdentifier.substr(0, commaPos);
+ std::string layoutType = layoutIdentifier.substr(commaPos + 1);
+ return KeyboardLayoutInfo(std::move(languageTag),
+ std::move(layoutType));
+ });
jint hoverTapTimeout = env->CallIntMethod(mServiceObj,
gServiceClassInfo.getHoverTapTimeout);
@@ -643,16 +657,18 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon
} // release lock
}
-std::unordered_map<std::string, std::string> NativeInputManager::readMapFromInterleavedJavaArray(
- jmethodID method, const char* methodName) {
+template <typename T>
+std::unordered_map<std::string, T> NativeInputManager::readMapFromInterleavedJavaArray(
+ jmethodID method, const char* methodName, std::function<T(std::string)> opOnValue) {
JNIEnv* env = jniEnv();
jobjectArray javaArray = jobjectArray(env->CallObjectMethod(mServiceObj, method));
- std::unordered_map<std::string, std::string> map;
+ std::unordered_map<std::string, T> map;
if (!checkAndClearExceptionFromCallback(env, methodName) && javaArray) {
jsize length = env->GetArrayLength(javaArray);
for (jsize i = 0; i < length / 2; i++) {
std::string key = getStringElementFromJavaArray(env, javaArray, 2 * i);
- std::string value = getStringElementFromJavaArray(env, javaArray, 2 * i + 1);
+ T value =
+ opOnValue(std::move(getStringElementFromJavaArray(env, javaArray, 2 * i + 1)));
map.insert({key, value});
}
}
@@ -2256,6 +2272,12 @@ static void nativeChangeTypeAssociation(JNIEnv* env, jobject nativeImplObj) {
InputReaderConfiguration::CHANGE_DEVICE_TYPE);
}
+static void changeKeyboardLayoutAssociation(JNIEnv* env, jobject nativeImplObj) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+ im->getInputManager()->getReader().requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUT_ASSOCIATION);
+}
+
static void nativeSetMotionClassifierEnabled(JNIEnv* env, jobject nativeImplObj, jboolean enabled) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -2445,6 +2467,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"notifyPortAssociationsChanged", "()V", (void*)nativeNotifyPortAssociationsChanged},
{"changeUniqueIdAssociation", "()V", (void*)nativeChangeUniqueIdAssociation},
{"changeTypeAssociation", "()V", (void*)nativeChangeTypeAssociation},
+ {"changeKeyboardLayoutAssociation", "()V", (void*)changeKeyboardLayoutAssociation},
{"setDisplayEligibilityForPointerCapture", "(IZ)V",
(void*)nativeSetDisplayEligibilityForPointerCapture},
{"setMotionClassifierEnabled", "(Z)V", (void*)nativeSetMotionClassifierEnabled},
@@ -2569,6 +2592,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.getDeviceTypeAssociations, clazz, "getDeviceTypeAssociations",
"()[Ljava/lang/String;");
+ GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutAssociations, clazz,
+ "getKeyboardLayoutAssociations", "()[Ljava/lang/String;");
+
GET_METHOD_ID(gServiceClassInfo.getKeyRepeatTimeout, clazz,
"getKeyRepeatTimeout", "()I");
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 f96c929375c5..7f85347b6731 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -74,6 +74,9 @@
<xs:element type="sensorDetails" name="lightSensor">
<xs:annotation name="final"/>
</xs:element>
+ <xs:element type="sensorDetails" name="screenOffBrightnessSensor">
+ <xs:annotation name="final"/>
+ </xs:element>
<xs:element type="sensorDetails" name="proxSensor">
<xs:annotation name="final"/>
</xs:element>
@@ -109,6 +112,11 @@
<xs:element type="thresholds" name="ambientBrightnessChangeThresholdsIdle" minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
+ <!-- Table that translates sensor values from the screenOffBrightnessSensor
+ to lux values; -1 means the lux reading is not available. -->
+ <xs:element type="integer-array" name="screenOffBrightnessSensorValueToLux">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -485,4 +493,10 @@
</xs:element>
</xs:sequence>
</xs:complexType>
+
+ <xs:complexType name="integer-array">
+ <xs:sequence>
+ <xs:element name="item" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 6276edaf3ebc..cb081791ffce 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -98,6 +98,8 @@ package com.android.server.display.config {
method public final java.math.BigInteger getScreenBrightnessRampIncreaseMaxMillis();
method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
+ method public final com.android.server.display.config.SensorDetails getScreenOffBrightnessSensor();
+ method public final com.android.server.display.config.IntegerArray getScreenOffBrightnessSensorValueToLux();
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);
@@ -120,6 +122,8 @@ package com.android.server.display.config {
method public final void setScreenBrightnessRampIncreaseMaxMillis(java.math.BigInteger);
method public final void setScreenBrightnessRampSlowDecrease(java.math.BigDecimal);
method public final void setScreenBrightnessRampSlowIncrease(java.math.BigDecimal);
+ method public final void setScreenOffBrightnessSensor(com.android.server.display.config.SensorDetails);
+ method public final void setScreenOffBrightnessSensorValueToLux(com.android.server.display.config.IntegerArray);
method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
}
@@ -160,6 +164,11 @@ package com.android.server.display.config {
method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
}
+ public class IntegerArray {
+ ctor public IntegerArray();
+ method public java.util.List<java.math.BigInteger> getItem();
+ }
+
public class NitsMap {
ctor public NitsMap();
method public String getInterpolation();
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index a92e4a18d609..b4d7632371e7 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -32,8 +32,8 @@ import android.credentials.ICreateCredentialCallback;
import android.credentials.ICredentialManager;
import android.credentials.IGetCredentialCallback;
import android.credentials.IListEnabledProvidersCallback;
-import android.credentials.ListEnabledProvidersResponse;
import android.credentials.ISetEnabledProvidersCallback;
+import android.credentials.ListEnabledProvidersResponse;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.ICancellationSignal;
@@ -41,7 +41,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.credentials.BeginCreateCredentialRequest;
-import android.service.credentials.BeginGetCredentialsRequest;
+import android.service.credentials.BeginGetCredentialRequest;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -183,15 +183,11 @@ public final class CredentialManagerService
// TODO : Return error when no providers available
// Iterate over all provider sessions and invoke the request
- providerSessions.forEach(
- providerGetSession -> {
- providerGetSession
- .getRemoteCredentialService()
- .onBeginGetCredentials(
- (BeginGetCredentialsRequest)
- providerGetSession.getProviderRequest(),
- /* callback= */ providerGetSession);
- });
+ providerSessions.forEach(providerGetSession -> {
+ providerGetSession.getRemoteCredentialService().onBeginGetCredential(
+ (BeginGetCredentialRequest) providerGetSession.getProviderRequest(),
+ /*callback=*/providerGetSession);
+ });
return cancelTransport;
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 8854453a61cd..32e85b099c22 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -22,15 +22,18 @@ import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.Signature;
import android.credentials.ui.CreateCredentialProviderData;
import android.credentials.ui.Entry;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.service.credentials.BeginCreateCredentialRequest;
import android.service.credentials.BeginCreateCredentialResponse;
+import android.service.credentials.CallingAppInfo;
import android.service.credentials.CreateCredentialRequest;
import android.service.credentials.CreateEntry;
import android.service.credentials.CredentialProviderInfo;
import android.service.credentials.CredentialProviderService;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -65,11 +68,12 @@ public final class ProviderCreateSession extends ProviderSession<
CreateCredentialRequest providerCreateRequest =
createProviderRequest(providerInfo.getCapabilities(),
createRequestSession.mClientRequest,
- createRequestSession.mClientCallingPackage);
+ new CallingAppInfo(createRequestSession.mClientCallingPackage,
+ new ArraySet<Signature>()));
if (providerCreateRequest != null) {
BeginCreateCredentialRequest providerBeginCreateRequest =
new BeginCreateCredentialRequest(
- providerCreateRequest.getCallingPackage(),
+ providerCreateRequest.getCallingAppInfo(),
providerCreateRequest.getType(),
createRequestSession.mClientRequest.getCandidateQueryData());
return new ProviderCreateSession(context, providerInfo, createRequestSession, userId,
@@ -82,10 +86,10 @@ public final class ProviderCreateSession extends ProviderSession<
@Nullable
private static CreateCredentialRequest createProviderRequest(List<String> providerCapabilities,
android.credentials.CreateCredentialRequest clientRequest,
- String clientCallingPackage) {
+ CallingAppInfo callingAppInfo) {
String capability = clientRequest.getType();
if (providerCapabilities.contains(capability)) {
- return new CreateCredentialRequest(clientCallingPackage, capability,
+ return new CreateCredentialRequest(callingAppInfo, capability,
clientRequest.getCredentialData());
}
Log.i(TAG, "Unable to create provider request - capabilities do not match");
@@ -120,7 +124,8 @@ public final class ProviderCreateSession extends ProviderSession<
/** Called when the provider response resulted in a failure. */
@Override
- public void onProviderResponseFailure(int errorCode, @Nullable CharSequence message) {
+ public void onProviderResponseFailure(int errorCode, @Nullable String errorType,
+ @Nullable CharSequence message) {
updateStatusAndInvokeCallback(toStatus(errorCode));
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 9888cc0a3732..218fc21e73e3 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -22,6 +22,7 @@ import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.Signature;
import android.credentials.GetCredentialOption;
import android.credentials.GetCredentialResponse;
import android.credentials.ui.Entry;
@@ -29,13 +30,15 @@ import android.credentials.ui.GetCredentialProviderData;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.service.credentials.Action;
import android.service.credentials.BeginGetCredentialOption;
-import android.service.credentials.BeginGetCredentialsRequest;
-import android.service.credentials.BeginGetCredentialsResponse;
+import android.service.credentials.BeginGetCredentialRequest;
+import android.service.credentials.BeginGetCredentialResponse;
+import android.service.credentials.CallingAppInfo;
import android.service.credentials.CredentialEntry;
import android.service.credentials.CredentialProviderInfo;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.CredentialsResponseContent;
import android.service.credentials.GetCredentialRequest;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -53,10 +56,10 @@ import java.util.stream.Collectors;
*
* @hide
*/
-public final class ProviderGetSession extends ProviderSession<BeginGetCredentialsRequest,
- BeginGetCredentialsResponse>
+public final class ProviderGetSession extends ProviderSession<BeginGetCredentialRequest,
+ BeginGetCredentialResponse>
implements
- RemoteCredentialService.ProviderCallbacks<BeginGetCredentialsResponse> {
+ RemoteCredentialService.ProviderCallbacks<BeginGetCredentialResponse> {
private static final String TAG = "ProviderGetSession";
// Key to be used as an entry key for a credential entry
@@ -90,9 +93,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
getRequestSession.mClientCallingPackage);
if (completeRequest != null) {
// TODO: Update to using query data when ready
- BeginGetCredentialsRequest beginGetCredentialsRequest =
- new BeginGetCredentialsRequest.Builder(
- completeRequest.getCallingPackage())
+ BeginGetCredentialRequest beginGetCredentialRequest =
+ new BeginGetCredentialRequest.Builder(
+ completeRequest.getCallingAppInfo())
.setBeginGetCredentialOptions(
completeRequest.getGetCredentialOptions().stream().map(
option -> {
@@ -104,7 +107,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
}).collect(Collectors.toList()))
.build();
return new ProviderGetSession(context, providerInfo, getRequestSession, userId,
- remoteCredentialService, beginGetCredentialsRequest, completeRequest);
+ remoteCredentialService, beginGetCredentialRequest, completeRequest);
}
Log.i(TAG, "Unable to create provider session");
return null;
@@ -126,7 +129,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
}
}
if (!filteredOptions.isEmpty()) {
- return new GetCredentialRequest.Builder(clientCallingPackage).setGetCredentialOptions(
+ return new GetCredentialRequest.Builder(
+ new CallingAppInfo(clientCallingPackage,
+ new ArraySet<Signature>())).setGetCredentialOptions(
filteredOptions).build();
}
Log.i(TAG, "In createProviderRequest - returning null");
@@ -137,7 +142,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
CredentialProviderInfo info,
ProviderInternalCallback callbacks,
int userId, RemoteCredentialService remoteCredentialService,
- BeginGetCredentialsRequest beginGetRequest,
+ BeginGetCredentialRequest beginGetRequest,
GetCredentialRequest completeGetRequest) {
super(context, info, beginGetRequest, callbacks, userId, remoteCredentialService);
mCompleteRequest = completeGetRequest;
@@ -152,14 +157,15 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
/** Called when the provider response has been updated by an external source. */
@Override // Callback from the remote provider
- public void onProviderResponseSuccess(@Nullable BeginGetCredentialsResponse response) {
+ public void onProviderResponseSuccess(@Nullable BeginGetCredentialResponse response) {
Log.i(TAG, "in onProviderResponseSuccess");
onUpdateResponse(response);
}
/** Called when the provider response resulted in a failure. */
@Override // Callback from the remote provider
- public void onProviderResponseFailure(int errorCode, @Nullable CharSequence message) {
+ public void onProviderResponseFailure(int errorCode, @Nullable String errorType,
+ @Nullable CharSequence message) {
updateStatusAndInvokeCallback(toStatus(errorCode));
}
@@ -349,7 +355,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
.getResultData());
if (content != null) {
onUpdateResponse(
- BeginGetCredentialsResponse.createWithResponseContent(content));
+ BeginGetCredentialResponse.createWithResponseContent(content));
return;
}
}
@@ -366,7 +372,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
/** Updates the response being maintained in state by this provider session. */
- private void onUpdateResponse(BeginGetCredentialsResponse response) {
+ private void onUpdateResponse(BeginGetCredentialResponse response) {
mProviderResponse = response;
if (response.getAuthenticationAction() != null) {
Log.i(TAG , "updateResponse with authentication entry");
diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
index 7a883b359d9f..307d96a3bded 100644
--- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
+++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
@@ -21,18 +21,19 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.credentials.CreateCredentialException;
+import android.credentials.GetCredentialException;
import android.os.Handler;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.service.credentials.BeginCreateCredentialRequest;
import android.service.credentials.BeginCreateCredentialResponse;
-import android.service.credentials.BeginGetCredentialsRequest;
-import android.service.credentials.BeginGetCredentialsResponse;
+import android.service.credentials.BeginGetCredentialRequest;
+import android.service.credentials.BeginGetCredentialResponse;
import android.service.credentials.CredentialProviderException;
-import android.service.credentials.CredentialProviderException.CredentialProviderError;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.IBeginCreateCredentialCallback;
-import android.service.credentials.IBeginGetCredentialsCallback;
+import android.service.credentials.IBeginGetCredentialCallback;
import android.service.credentials.ICredentialProviderService;
import android.text.format.DateUtils;
import android.util.Log;
@@ -71,7 +72,8 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
/** Called when a successful response is received from the remote provider. */
void onProviderResponseSuccess(@Nullable T response);
/** Called when a failure response is received from the remote provider. */
- void onProviderResponseFailure(int errorCode, @Nullable CharSequence message);
+ void onProviderResponseFailure(int errorCode, @Nullable String errorType,
+ @Nullable CharSequence message);
/** Called when the remote provider service dies. */
void onProviderServiceDied(RemoteCredentialService service);
}
@@ -106,35 +108,34 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
* @param callback the callback to be used to send back the provider response to the
* {@link ProviderGetSession} class that maintains provider state
*/
- public void onBeginGetCredentials(@NonNull BeginGetCredentialsRequest request,
- ProviderCallbacks<BeginGetCredentialsResponse> callback) {
+ public void onBeginGetCredential(@NonNull BeginGetCredentialRequest request,
+ ProviderCallbacks<BeginGetCredentialResponse> callback) {
Log.i(TAG, "In onGetCredentials in RemoteCredentialService");
AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
- AtomicReference<CompletableFuture<BeginGetCredentialsResponse>> futureRef =
+ AtomicReference<CompletableFuture<BeginGetCredentialResponse>> futureRef =
new AtomicReference<>();
- CompletableFuture<BeginGetCredentialsResponse> connectThenExecute = postAsync(service -> {
- CompletableFuture<BeginGetCredentialsResponse> getCredentials =
+ CompletableFuture<BeginGetCredentialResponse> connectThenExecute = postAsync(service -> {
+ CompletableFuture<BeginGetCredentialResponse> getCredentials =
new CompletableFuture<>();
ICancellationSignal cancellationSignal =
- service.onBeginGetCredentials(request,
- new IBeginGetCredentialsCallback.Stub() {
+ service.onBeginGetCredential(request,
+ new IBeginGetCredentialCallback.Stub() {
@Override
- public void onSuccess(BeginGetCredentialsResponse response) {
+ public void onSuccess(BeginGetCredentialResponse response) {
Log.i(TAG, "In onSuccess in RemoteCredentialService");
getCredentials.complete(response);
}
@Override
- public void onFailure(@CredentialProviderError int errorCode,
- CharSequence message) {
+ public void onFailure(String errorType, CharSequence message) {
Log.i(TAG, "In onFailure in RemoteCredentialService");
String errorMsg = message == null ? "" : String.valueOf(message);
- getCredentials.completeExceptionally(new CredentialProviderException(
- errorCode, errorMsg));
+ getCredentials.completeExceptionally(
+ new GetCredentialException(errorType, errorMsg));
}
});
- CompletableFuture<BeginGetCredentialsResponse> future = futureRef.get();
+ CompletableFuture<BeginGetCredentialResponse> future = futureRef.get();
if (future != null && future.isCancelled()) {
dispatchCancellationSignal(cancellationSignal);
} else {
@@ -175,12 +176,11 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
}
@Override
- public void onFailure(@CredentialProviderError int errorCode,
- CharSequence message) {
+ public void onFailure(String errorType, CharSequence message) {
Log.i(TAG, "In onFailure in RemoteCredentialService");
String errorMsg = message == null ? "" : String.valueOf(message);
createCredentialFuture.completeExceptionally(
- new CredentialProviderException(errorCode, errorMsg));
+ new CreateCredentialException(errorType, errorMsg));
}});
CompletableFuture<BeginCreateCredentialResponse> future = futureRef.get();
if (future != null && future.isCancelled()) {
@@ -209,22 +209,34 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
dispatchCancellationSignal(cancellationSink.get());
callback.onProviderResponseFailure(
CredentialProviderException.ERROR_TIMEOUT,
+ null,
error.getMessage());
} else if (error instanceof CancellationException) {
Log.i(TAG, "In RemoteCredentialService execute error is cancellation");
dispatchCancellationSignal(cancellationSink.get());
callback.onProviderResponseFailure(
CredentialProviderException.ERROR_TASK_CANCELED,
+ null,
error.getMessage());
- } else if (error instanceof CredentialProviderException) {
- Log.i(TAG, "In RemoteCredentialService execute error is provider error");
- callback.onProviderResponseFailure(((CredentialProviderException) error)
- .getErrorCode(),
+ } else if (error instanceof GetCredentialException) {
+ Log.i(TAG, "In RemoteCredentialService execute error is provider get"
+ + "error");
+ callback.onProviderResponseFailure(
+ CredentialProviderException.ERROR_PROVIDER_FAILURE,
+ ((GetCredentialException) error).errorType,
+ error.getMessage());
+ } else if (error instanceof CreateCredentialException) {
+ Log.i(TAG, "In RemoteCredentialService execute error is provider create "
+ + "error");
+ callback.onProviderResponseFailure(
+ CredentialProviderException.ERROR_PROVIDER_FAILURE,
+ ((CreateCredentialException) error).errorType,
error.getMessage());
} else {
Log.i(TAG, "In RemoteCredentialService execute error is unknown");
callback.onProviderResponseFailure(
CredentialProviderException.ERROR_UNKNOWN,
+ null,
error.getMessage());
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c42ddf81dfd4..667f41f0ad4d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -721,6 +721,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
+ // TODO(b/261999445) remove the flag after rollout.
+ private static final String HEADLESS_FLAG = "headless";
+ private static final boolean DEFAULT_HEADLESS_FLAG = true;
+
/**
* This feature flag is checked once after boot and this value us used until the next reboot to
* avoid needing to handle the flag changing on the fly.
@@ -1231,6 +1235,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
} catch (RemoteException re) {
// Shouldn't happen.
+ Slogf.wtf(LOG_TAG, "Error handling package changes", re);
}
}
if (removedAdmin) {
@@ -1288,6 +1293,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
&& mIPackageManager.getPackageInfo(targetPackage, 0, userHandle) == null;
} catch (RemoteException e) {
// Shouldn't happen
+ Slogf.wtf(LOG_TAG, "Error checking isRemovedPackage", e);
}
return false;
@@ -1310,6 +1316,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
null, null, null, PLATFORM_PACKAGE_NAME, userHandle);
} catch (RemoteException ignored) {
// shouldn't happen.
+ Slogf.wtf(LOG_TAG, "Error handling new package installed", ignored);
}
}
@@ -2228,6 +2235,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
} catch (RemoteException e) {
// Shouldn't happen.
+ Slogf.wtf(LOG_TAG, "Error getting application info", e);
return;
}
if (appInfo == null) {
@@ -2580,7 +2588,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ensureLocked();
// Try to find an admin which can use reqPolicy
final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(userId);
- final ComponentName doAdminComponent = mOwners.getDeviceOwnerComponent();
if (poAdminComponent != null) {
return getProfileOwnerLocked(userId);
@@ -2862,6 +2869,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
} catch (RemoteException e) {
// shouldn't happen.
+ Slogf.wtf(LOG_TAG, "Error getting receiver info", e);
return null;
}
});
@@ -6769,6 +6777,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
} catch (RemoteException re) {
// Shouldn't happen
+ Slogf.wtf(LOG_TAG, "Error forcing wipe user", re);
} finally {
if (!success) SecurityLog.writeEvent(SecurityLog.TAG_WIPE_FAILURE);
}
@@ -7063,8 +7072,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(frpManagementAgentUid == caller.getUid()
|| hasCallingPermission(permission.MASTER_CLEAR),
"Must be called by the FRP management agent on device");
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.getUserId(frpManagementAgentUid));
+ // TODO(b/261999445): Remove
+ if (isHeadlessFlagEnabled()) {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+ } else {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.getUserId(frpManagementAgentUid));
+ }
} else {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
@@ -7105,8 +7119,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
hasCallingOrSelfPermission(permission.TRIGGER_LOST_MODE));
synchronized (getLockObject()) {
- final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
+ // TODO(b/261999445): Remove
+ ActiveAdmin admin;
+ if (isHeadlessFlagEnabled()) {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+ } else {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ }
Preconditions.checkState(admin != null,
"Lost mode location updates can only be sent on an organization-owned device.");
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -7741,9 +7761,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// be disabled device-wide.
private void pushScreenCapturePolicy(int adminUserId) {
// Update screen capture device-wide if disabled by the DO or COPE PO on the parent profile.
- ActiveAdmin admin =
- getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(
- UserHandle.USER_SYSTEM);
+ // TODO(b/261999445): remove
+ ActiveAdmin admin;
+ if (isHeadlessFlagEnabled()) {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(
+ mUserManagerInternal.getProfileParentId(adminUserId));
+ } else {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(
+ UserHandle.USER_SYSTEM);
+ }
if (admin != null && admin.disableScreenCapture) {
setScreenCaptureDisabled(UserHandle.USER_ALL);
} else {
@@ -8774,6 +8800,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
+ /**
+ * @deprecated Use the version which does not take a user id.
+ */
+ @Deprecated
ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(int userId) {
ensureLocked();
ActiveAdmin admin = getDeviceOwnerAdminLocked();
@@ -8783,6 +8813,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return admin;
}
+ ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked() {
+ ensureLocked();
+ ActiveAdmin admin = getDeviceOwnerAdminLocked();
+ if (admin == null) {
+ admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
+ }
+ return admin;
+ }
+
ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(int userId) {
ensureLocked();
ActiveAdmin admin = getDeviceOwnerAdminLocked();
@@ -9078,6 +9117,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pushUserRestrictions(userId);
} catch (RemoteException re) {
// Shouldn't happen.
+ Slogf.wtf(LOG_TAG, "Failing in updatePermissionFlagsForAllApps", re);
}
}
@@ -9341,6 +9381,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
}
+ @GuardedBy("getLockObject()")
+ ActiveAdmin getProfileOwnerOfOrganizationOwnedDeviceLocked() {
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ if (userInfo.isManagedProfile()) {
+ if (getProfileOwnerAsUser(userInfo.id) != null
+ && isProfileOwnerOfOrganizationOwnedDevice(userInfo.id)) {
+ ComponentName who = getProfileOwnerAsUser(userInfo.id);
+ return getActiveAdminUncheckedLocked(who, userInfo.id);
+ }
+ }
+ }
+ return null;
+ });
+ }
+
/**
* This API is cached: invalidate with invalidateBinderCaches().
*/
@@ -9797,7 +9853,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
return currentUser.id;
} catch (RemoteException e) {
- Slogf.wtf(LOG_TAG, "cannot get current user");
+ Slogf.wtf(LOG_TAG, "cannot get current user", e);
}
return UserHandle.USER_NULL;
}
@@ -10027,6 +10083,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mIPackageManager.flushPackageRestrictionsAsUser(userHandle);
} catch (RemoteException re) {
// Shouldn't happen
+ Slog.wtf(LOG_TAG, "Error adding persistent preferred activity", re);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -10055,6 +10112,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mIPackageManager.flushPackageRestrictionsAsUser(userHandle);
} catch (RemoteException re) {
// Shouldn't happen
+ Slogf.wtf(
+ LOG_TAG, "Error when clearing package persistent preferred activities", re);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -10252,6 +10311,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
} catch (RemoteException re) {
// Shouldn't happen
+ Slogf.wtf(LOG_TAG, "Error adding cross profile intent filter", re);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -10302,6 +10362,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mIPackageManager.clearCrossProfileIntentFilters(parent.id, who.getPackageName());
} catch (RemoteException re) {
// Shouldn't happen
+ Slogf.wtf(LOG_TAG, "Error clearing cross profile intent filters", re);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -11826,7 +11887,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
} catch (RemoteException e) {
// shouldn't happen
- Slogf.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
+ Slogf.wtf(LOG_TAG, "Failed to resolve intent for: " + intent, e);
return 0;
} finally {
mInjector.binderRestoreCallingIdentity(id);
@@ -11880,6 +11941,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
== PackageManager.INSTALL_SUCCEEDED;
} catch (RemoteException re) {
// shouldn't happen
+ Slogf.wtf(LOG_TAG, "Error installing package", re);
return false;
} finally {
mInjector.binderRestoreCallingIdentity(id);
@@ -14523,6 +14585,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return ai == null ? 0 : ai.targetSdkVersion;
} catch (RemoteException e) {
// Shouldn't happen
+ Slogf.wtf(LOG_TAG, "Error getting application info", e);
return 0;
}
}
@@ -17415,8 +17478,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
// Only DO or COPE PO can turn on CC mode, so take a shortcut here and only look at
// their ActiveAdmin, instead of iterating through all admins.
- final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
+ ActiveAdmin admin;
+ // TODO(b/261999445): remove
+ if (isHeadlessFlagEnabled()) {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+ } else {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ }
return admin != null ? admin.mCommonCriteriaMode : false;
}
}
@@ -18233,6 +18302,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
} catch (RemoteException e) {
// Shouldn't happen.
+ Slogf.wtf(LOG_TAG, "Error setting application enabled", e);
}
}
@@ -18268,6 +18338,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
callerPackage);
} catch (RemoteException e) {
// Shouldn't happen.
+ Slogf.wtf(LOG_TAG, "Error starting user", e);
} finally {
mContext.unregisterReceiver(unlockedReceiver);
}
@@ -18590,6 +18661,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
} catch (RemoteException e) {
// Shouldn't happen.
+ Slogf.wtf(LOG_TAG, "Error resetting default cross profile intent filters", e);
}
});
}
@@ -18819,8 +18891,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean isUsbDataSignalingEnabledInternalLocked() {
- final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
+ // TODO(b/261999445): remove
+ ActiveAdmin admin;
+ if (isHeadlessFlagEnabled()) {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+ } else {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ }
return admin == null || admin.mUsbDataSignalingEnabled;
}
@@ -18870,8 +18948,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getMinimumRequiredWifiSecurityLevel() {
synchronized (getLockObject()) {
- final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
+ ActiveAdmin admin;
+ // TODO(b/261999445): remove
+ if (isHeadlessFlagEnabled()) {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+ } else {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ }
return (admin == null) ? DevicePolicyManager.WIFI_SECURITY_OPEN
: admin.mWifiMinimumSecurityLevel;
}
@@ -18887,8 +18971,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ "a profile owner on an organization-owned device or "
+ "an app with the QUERY_ADMIN_POLICY permission.");
synchronized (getLockObject()) {
- final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
+ ActiveAdmin admin;
+ // TODO(b/261999445): remove
+ if (isHeadlessFlagEnabled()) {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+ } else {
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ }
return admin != null ? admin.mWifiSsidPolicy : null;
}
}
@@ -19260,9 +19350,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|| isProfileOwnerOfOrganizationOwnedDevice(caller));
}
synchronized (getLockObject()) {
- ActiveAdmin admin =
- getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
+ // TODO(b/261999445): Remove
+ ActiveAdmin admin;
+ if (isHeadlessFlagEnabled()) {
+ admin =
+ getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+ } else {
+ admin =
+ getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ }
+
if (admin != null) {
final String memtagProperty = "arm64.memtag.bootctl";
if (flags == DevicePolicyManager.MTE_ENABLED) {
@@ -19284,12 +19382,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|| isProfileOwnerOfOrganizationOwnedDevice(caller)
|| isSystemUid(caller));
synchronized (getLockObject()) {
- ActiveAdmin admin =
- getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
+ // TODO(b/261999445): Remove
+ ActiveAdmin admin;
+ if (isHeadlessFlagEnabled()) {
+ admin =
+ getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+ } else {
+ admin =
+ getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ }
return admin != null
? admin.mtePolicy
: DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
}
}
-}
+
+ private boolean isHeadlessFlagEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_DEVICE_POLICY_MANAGER,
+ HEADLESS_FLAG,
+ DEFAULT_HEADLESS_FLAG);
+ }
+} \ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/AccessPersistence.kt b/services/permission/java/com/android/server/permission/access/AccessPersistence.kt
index 022f09a028db..91239c692505 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPersistence.kt
@@ -33,24 +33,23 @@ class AccessPersistence(
private val policy: AccessPolicy
) {
fun read(state: AccessState) {
- readSystemState(state.systemState)
- val userStates = state.userStates
+ readSystemState(state)
state.systemState.userIds.forEachIndexed { _, userId ->
- readUserState(userId, userStates[userId])
+ readUserState(state, userId)
}
}
- private fun readSystemState(systemState: SystemState) {
+ private fun readSystemState(state: AccessState) {
systemFile.parse {
// This is the canonical way to call an extension function in a different class.
// TODO(b/259469752): Use context receiver for this when it becomes stable.
- with(policy) { parseSystemState(systemState) }
+ with(policy) { parseSystemState(state) }
}
}
- private fun readUserState(userId: Int, userState: UserState) {
+ private fun readUserState(state: AccessState, userId: Int) {
getUserFile(userId).parse {
- with(policy) { parseUserState(userId, userState) }
+ with(policy) { parseUserState(state, userId) }
}
}
@@ -65,30 +64,30 @@ class AccessPersistence(
}
fun write(state: AccessState) {
- writeState(state.systemState, ::writeSystemState)
+ writeState(state.systemState) { writeSystemState(state) }
state.userStates.forEachIndexed { _, userId, userState ->
- writeState(userState) { writeUserState(userId, it) }
+ writeState(userState) { writeUserState(state, userId) }
}
}
- private inline fun <T : WritableState> writeState(state: T, write: (T) -> Unit) {
+ private inline fun <T : WritableState> writeState(state: T, write: () -> Unit) {
when (val writeMode = state.writeMode) {
WriteMode.NONE -> {}
- WriteMode.SYNC -> write(state)
+ WriteMode.SYNC -> write()
WriteMode.ASYNC -> TODO()
else -> error(writeMode)
}
}
- private fun writeSystemState(systemState: SystemState) {
+ private fun writeSystemState(state: AccessState) {
systemFile.serialize {
- with(policy) { serializeSystemState(systemState) }
+ with(policy) { serializeSystemState(state) }
}
}
- private fun writeUserState(userId: Int, userState: UserState) {
+ private fun writeUserState(state: AccessState, userId: Int) {
getUserFile(userId).serialize {
- with(policy) { serializeUserState(userId, userState) }
+ with(policy) { serializeUserState(state, userId) }
}
}
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 89316c2812dd..8027b50fe254 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -202,13 +202,13 @@ class AccessPolicy private constructor(
}
}
- fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) {
+ fun BinaryXmlPullParser.parseSystemState(state: AccessState) {
forEachTag {
when (tagName) {
TAG_ACCESS -> {
forEachTag {
forEachSchemePolicy {
- with(it) { parseSystemState(systemState) }
+ with(it) { parseSystemState(state) }
}
}
}
@@ -217,21 +217,21 @@ class AccessPolicy private constructor(
}
}
- fun BinaryXmlSerializer.serializeSystemState(systemState: SystemState) {
+ fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {
tag(TAG_ACCESS) {
forEachSchemePolicy {
- with(it) { serializeSystemState(systemState) }
+ with(it) { serializeSystemState(state) }
}
}
}
- fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {
+ fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
forEachTag {
when (tagName) {
TAG_ACCESS -> {
forEachTag {
forEachSchemePolicy {
- with(it) { parseUserState(userId, userState) }
+ with(it) { parseUserState(state, userId) }
}
}
}
@@ -245,10 +245,10 @@ class AccessPolicy private constructor(
}
}
- fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {
+ fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
tag(TAG_ACCESS) {
forEachSchemePolicy {
- with(it) { serializeUserState(userId, userState) }
+ with(it) { serializeUserState(state, userId) }
}
}
}
@@ -305,11 +305,11 @@ abstract class SchemePolicy {
open fun MutateStateScope.onPackageUninstalled(packageName: String, appId: Int, userId: Int) {}
- open fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) {}
+ open fun BinaryXmlPullParser.parseSystemState(state: AccessState) {}
- open fun BinaryXmlSerializer.serializeSystemState(systemState: SystemState) {}
+ open fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {}
- open fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {}
+ open fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {}
- open fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {}
+ open fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {}
}
diff --git a/services/permission/java/com/android/server/permission/access/AccessState.kt b/services/permission/java/com/android/server/permission/access/AccessState.kt
index fae1ec3ba702..6924d5139737 100644
--- a/services/permission/java/com/android/server/permission/access/AccessState.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessState.kt
@@ -46,6 +46,8 @@ class SystemState private constructor(
val knownPackages: IntMap<IndexedListSet<String>>,
// A map of userId to packageName
val deviceAndProfileOwners: IntMap<String>,
+ // Whether the device supports leanback UI
+ var isLeanback: Boolean,
val privilegedPermissionAllowlistSourcePackageNames: IndexedListSet<String>,
var permissionAllowlist: PermissionAllowlist,
val implicitToSourcePermissions: Map<String, Set<String>>,
@@ -60,6 +62,7 @@ class SystemState private constructor(
IntMap(),
IntMap(),
IntMap(),
+ false,
IndexedListSet(),
PermissionAllowlist(),
IndexedMap(),
@@ -76,6 +79,7 @@ class SystemState private constructor(
appIds.copy { it.copy() },
knownPackages.copy { it.copy() },
deviceAndProfileOwners.copy { it },
+ isLeanback,
privilegedPermissionAllowlistSourcePackageNames.copy(),
permissionAllowlist,
implicitToSourcePermissions,
@@ -88,7 +92,9 @@ class SystemState private constructor(
class UserState private constructor(
// A map of (appId to a map of (permissionName to permissionFlags))
val uidPermissionFlags: IntMap<IndexedMap<String, Int>>,
+ // appId -> opName -> opCode
val uidAppOpModes: IntMap<IndexedMap<String, Int>>,
+ // packageName -> opName -> opCode
val packageAppOpModes: IndexedMap<String, IndexedMap<String, Int>>
) : WritableState() {
constructor() : this(
@@ -125,11 +131,11 @@ abstract class WritableState {
}
}
-class GetStateScope(
+open class GetStateScope(
val state: AccessState
)
class MutateStateScope(
val oldState: AccessState,
val newState: AccessState
-)
+) : GetStateScope(newState)
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index b8d6aa3b4e49..f2cff62d3b8c 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -16,88 +16,214 @@
package com.android.server.permission.access.appop
-import android.util.ArraySet
+import android.Manifest
+import android.annotation.UserIdInt
+import android.app.AppGlobals
+import android.app.AppOpsManager
+import android.content.pm.PackageManager
+import android.os.Binder
+import android.os.Handler
+import android.os.RemoteException
+import android.os.UserHandle
import android.util.SparseBooleanArray
import android.util.SparseIntArray
+import com.android.internal.util.ArrayUtils
+import com.android.internal.util.function.pooled.PooledLambda
import com.android.server.appop.AppOpsCheckingServiceInterface
import com.android.server.appop.OnOpModeChangedListener
import com.android.server.permission.access.AccessCheckingService
+import com.android.server.permission.access.AppOpUri
+import com.android.server.permission.access.PackageUri
+import com.android.server.permission.access.UidUri
+import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.util.hasBits
+import libcore.util.EmptyArray
import java.io.PrintWriter
class AppOpService(
private val service: AccessCheckingService
) : AppOpsCheckingServiceInterface {
+ private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME)
+ as PackageAppOpPolicy
+ private val uidPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME)
+ as UidAppOpPolicy
+
+ private val context = service.context
+ private lateinit var handler: Handler
+ private lateinit var lock: Any
+ private lateinit var switchedOps: IntMap<IntArray>
+
fun initialize() {
- TODO("Not yet implemented")
+ // TODO(b/252883039): Wrong handler. Inject main thread handler here.
+ handler = Handler(context.mainLooper)
+ // TODO(b/252883039): Wrong lock object. Inject AppOpsService here.
+ lock = Any()
+
+ switchedOps = IntMap()
+ for (switchedCode in 0 until AppOpsManager._NUM_OP) {
+ val switchCode = AppOpsManager.opToSwitch(switchedCode)
+ switchedOps.put(switchCode,
+ ArrayUtils.appendInt(switchedOps.get(switchCode), switchedCode))
+ }
}
override fun getNonDefaultUidModes(uid: Int): SparseIntArray {
- TODO("Not yet implemented")
+ return opNameMapToOpIntMap(getUidModes(uid))
}
override fun getUidMode(uid: Int, op: Int): Int {
- TODO("Not yet implemented")
+ val appId = UserHandle.getAppId(uid)
+ val userId = UserHandle.getUserId(uid)
+ val opName = AppOpsManager.opToPublicName(op)
+ return service.getState {
+ with(uidPolicy) { getAppOpMode(appId, userId, opName) }
+ }
+ }
+
+ private fun getUidModes(uid: Int): IndexedMap<String, Int>? {
+ val appId = UserHandle.getAppId(uid)
+ val userId = UserHandle.getUserId(uid)
+ return service.getState {
+ with(uidPolicy) { getAppOpModes(appId, userId) }
+ }
}
override fun setUidMode(uid: Int, op: Int, mode: Int): Boolean {
- TODO("Not yet implemented")
+ val appId = UserHandle.getAppId(uid)
+ val userId = UserHandle.getUserId(uid)
+ val opName = AppOpsManager.opToPublicName(op)
+ var wasChanged = false
+ service.mutateState {
+ wasChanged = with(uidPolicy) { setAppOpMode(appId, userId, opName, mode) }
+ }
+ return wasChanged
}
override fun getPackageMode(packageName: String, op: Int, userId: Int): Int {
- TODO("Not yet implemented")
+ val opName = AppOpsManager.opToPublicName(op)
+ return service.getState {
+ with(packagePolicy) { getAppOpMode(packageName, userId, opName) }
+ }
}
+ private fun getPackageModes(
+ packageName: String,
+ userId: Int
+ ): IndexedMap<String, Int>? =
+ service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }
+
override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) {
- TODO("Not yet implemented")
+ val opName = AppOpsManager.opToPublicName(op)
+ service.mutateState {
+ with(packagePolicy) { setAppOpMode(packageName, userId, opName, mode) }
+ }
}
- override fun removePackage(packageName: String, userId: Int): Boolean {
- TODO("Not yet implemented")
+ override fun removeUid(uid: Int) {
+ val appId = UserHandle.getAppId(uid)
+ val userId = UserHandle.getUserId(uid)
+ service.mutateState {
+ with(uidPolicy) { removeAppOpModes(appId, userId) }
+ }
}
- override fun removeUid(uid: Int) {
- TODO("Not yet implemented")
+ override fun removePackage(packageName: String, userId: Int): Boolean {
+ var wasChanged = false
+ service.mutateState {
+ wasChanged = with (packagePolicy) { removeAppOpModes(packageName, userId) }
+ }
+ return wasChanged
}
+ private fun opNameMapToOpIntMap(modes: IndexedMap<String, Int>?): SparseIntArray =
+ if (modes == null) {
+ SparseIntArray()
+ } else {
+ val opIntMap = SparseIntArray(modes.size)
+ modes.forEachIndexed { _, opName, opMode ->
+ opIntMap.put(AppOpsManager.strOpToOp(opName), opMode)
+ }
+ opIntMap
+ }
+
override fun areUidModesDefault(uid: Int): Boolean {
- TODO("Not yet implemented")
+ val modes = getUidModes(uid)
+ return modes == null || modes.isEmpty()
}
override fun arePackageModesDefault(packageName: String, userId: Int): Boolean {
- TODO("Not yet implemented")
+ val modes = service.getState { getPackageModes(packageName, userId) }
+ return modes == null || modes.isEmpty()
}
override fun clearAllModes() {
- TODO("Not yet implemented")
+ // We don't need to implement this because it's only called in AppOpsService#readState
+ // and we have our own persistence.
}
+ // code -> listeners
+ private val opModeWatchers = IntMap<IndexedSet<OnOpModeChangedListener>>()
+
+ // packageName -> listeners
+ private val packageModeWatchers = IndexedMap<String, IndexedSet<OnOpModeChangedListener>>()
+
override fun startWatchingOpModeChanged(changedListener: OnOpModeChangedListener, op: Int) {
- TODO("Not yet implemented")
+ synchronized(lock) {
+ opModeWatchers.getOrPut(op) { IndexedSet() } += changedListener
+ }
}
override fun startWatchingPackageModeChanged(
changedListener: OnOpModeChangedListener,
packageName: String
) {
- TODO("Not yet implemented")
+ synchronized(lock) {
+ packageModeWatchers.getOrPut(packageName) { IndexedSet() } += changedListener
+ }
}
override fun removeListener(changedListener: OnOpModeChangedListener) {
- TODO("Not yet implemented")
+ synchronized(lock) {
+ opModeWatchers.removeAllIndexed { _, _, listeners ->
+ listeners -= changedListener
+ listeners.isEmpty()
+ }
+ packageModeWatchers.removeAllIndexed { _, _, listeners ->
+ listeners -= changedListener
+ listeners.isEmpty()
+ }
+ }
}
- override fun getOpModeChangedListeners(op: Int): ArraySet<OnOpModeChangedListener> {
- TODO("Not yet implemented")
+ override fun getOpModeChangedListeners(op: Int): IndexedSet<OnOpModeChangedListener> {
+ synchronized(lock) {
+ val listeners = opModeWatchers[op]
+ return if (listeners == null) {
+ IndexedSet()
+ } else {
+ IndexedSet(listeners)
+ }
+ }
}
override fun getPackageModeChangedListeners(
packageName: String
- ): ArraySet<OnOpModeChangedListener> {
- TODO("Not yet implemented")
+ ): IndexedSet<OnOpModeChangedListener> {
+ synchronized(lock) {
+ val listeners = packageModeWatchers[packageName]
+ return if (listeners == null) {
+ IndexedSet()
+ } else {
+ IndexedSet(listeners)
+ }
+ }
}
override fun notifyWatchersOfChange(op: Int, uid: Int) {
- TODO("Not yet implemented")
+ val listeners = getOpModeChangedListeners(op)
+ listeners.forEachIndexed { _, listener ->
+ notifyOpChanged(listener, op, uid, null)
+ }
}
override fun notifyOpChanged(
@@ -106,31 +232,159 @@ class AppOpService(
uid: Int,
packageName: String?
) {
- TODO("Not yet implemented")
+ if (uid != UID_ANY &&
+ changedListener.watchingUid >= 0 &&
+ changedListener.watchingUid != uid
+ ) {
+ return
+ }
+
+ // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
+ val switchedCodes = when (changedListener.watchedOpCode) {
+ ALL_OPS -> switchedOps.get(op)
+ AppOpsManager.OP_NONE -> intArrayOf(op)
+ else -> intArrayOf(changedListener.watchedOpCode)
+ }
+
+ for (switchedCode in switchedCodes) {
+ // There are features watching for mode changes such as window manager
+ // and location manager which are in our process. The callbacks in these
+ // features may require permissions our remote caller does not have.
+ val identity = Binder.clearCallingIdentity()
+ try {
+ if (!shouldIgnoreCallback(switchedCode, changedListener)) {
+ changedListener.onOpModeChanged(switchedCode, uid, packageName)
+ }
+ } catch (e: RemoteException) {
+ /* ignore */
+ } finally {
+ Binder.restoreCallingIdentity(identity)
+ }
+ }
}
+ private fun shouldIgnoreCallback(op: Int, listener: OnOpModeChangedListener): Boolean {
+ // If it's a restricted read op, ignore it if watcher doesn't have manage ops permission,
+ // as watcher should not use this to signal if the value is changed.
+ return AppOpsManager.opRestrictsRead(op) && context.checkPermission(
+ Manifest.permission.MANAGE_APPOPS,
+ listener.callingPid,
+ listener.callingUid
+ ) != PackageManager.PERMISSION_GRANTED
+ }
+
+ /**
+ * Construct a map from each listener (listening to the given op, uid) to all of its associated
+ * packageNames (by reverse-indexing opModeWatchers and packageModeWatchers), then invoke
+ * notifyOpChanged for each listener.
+ */
override fun notifyOpChangedForAllPkgsInUid(
op: Int,
uid: Int,
onlyForeground: Boolean,
callbackToIgnore: OnOpModeChangedListener?
) {
- TODO("Not yet implemented")
+ val uidPackageNames = getPackagesForUid(uid)
+ val callbackSpecs = IndexedMap<OnOpModeChangedListener, IndexedSet<String>>()
+
+ fun associateListenerWithPackageNames(
+ listener: OnOpModeChangedListener,
+ packageNames: Array<String>
+ ) {
+ val listenerIsForeground =
+ listener.flags.hasBits(AppOpsManager.WATCH_FOREGROUND_CHANGES)
+ if (onlyForeground && !listenerIsForeground) {
+ return
+ }
+ val changedPackages = callbackSpecs.getOrPut(listener) { IndexedSet() }
+ changedPackages.addAll(packageNames)
+ }
+
+ synchronized(lock) {
+ // Collect all listeners from opModeWatchers and pckageModeWatchers
+ val listeners = opModeWatchers[op]
+ listeners?.forEachIndexed { _, listener ->
+ associateListenerWithPackageNames(listener, uidPackageNames)
+ }
+ uidPackageNames.forEachIndexed { _, uidPackageName ->
+ val packageListeners = packageModeWatchers[uidPackageName]
+ packageListeners?.forEachIndexed { _, listener ->
+ associateListenerWithPackageNames(listener, arrayOf(uidPackageName))
+ }
+ }
+ // Remove ignored listeners
+ if (callbackToIgnore != null) {
+ callbackSpecs.remove(callbackToIgnore)
+ }
+ }
+
+ // For each (listener, packageName) pair, invoke notifyOpChanged
+ callbackSpecs.forEachIndexed { _, listener, reportedPackageNames ->
+ reportedPackageNames.forEachIndexed { _, reportedPackageName ->
+ handler.sendMessage(
+ PooledLambda.obtainMessage(
+ AppOpService::notifyOpChanged, this, listener,
+ op, uid, reportedPackageName
+ )
+ )
+ }
+ }
+ }
+
+ private fun getPackagesForUid(uid: Int): Array<String> {
+ // Very early during boot the package manager is not yet or not yet fully started. At this
+ // time there are no packages yet.
+ return try {
+ AppGlobals.getPackageManager()?.getPackagesForUid(uid) ?: EmptyArray.STRING
+ } catch (e: RemoteException) {
+ EmptyArray.STRING
+ }
}
override fun evalForegroundUidOps(
uid: Int,
foregroundOps: SparseBooleanArray?
- ): SparseBooleanArray {
- TODO("Not yet implemented")
+ ): SparseBooleanArray? {
+ synchronized(lock) {
+ val uidModes = getUidModes(uid)
+ return evalForegroundOps(uidModes, foregroundOps)
+ }
}
override fun evalForegroundPackageOps(
packageName: String,
foregroundOps: SparseBooleanArray?,
- userId: Int
- ): SparseBooleanArray {
- TODO("Not yet implemented")
+ @UserIdInt userId: Int
+ ): SparseBooleanArray? {
+ synchronized(lock) {
+ val ops = service.getState { getPackageModes(packageName, userId) }
+ return evalForegroundOps(ops, foregroundOps)
+ }
+ }
+
+ private fun evalForegroundOps(
+ ops: IndexedMap<String, Int>?,
+ foregroundOps: SparseBooleanArray?
+ ): SparseBooleanArray? {
+ var foregroundOps = foregroundOps
+ ops?.forEachIndexed { _, opName, opMode ->
+ if (opMode == AppOpsManager.MODE_FOREGROUND) {
+ if (foregroundOps == null) {
+ foregroundOps = SparseBooleanArray()
+ }
+ evalForegroundWatchers(opName, foregroundOps!!)
+ }
+ }
+ return foregroundOps
+ }
+
+ private fun evalForegroundWatchers(opName: String, foregroundOps: SparseBooleanArray) {
+ val opCode = AppOpsManager.strOpToOp(opName)
+ val listeners = opModeWatchers[opCode]
+ val hasForegroundListeners = foregroundOps[opCode] || listeners?.anyIndexed { _, listener ->
+ listener.flags.hasBits(AppOpsManager.WATCH_FOREGROUND_CHANGES)
+ } ?: false
+ foregroundOps.put(opCode, hasForegroundListeners)
}
override fun dumpListeners(
@@ -139,10 +393,76 @@ class AppOpService(
dumpPackage: String?,
printWriter: PrintWriter
): Boolean {
- TODO("Not yet implemented")
+ var needSep = false
+ if (opModeWatchers.size() > 0) {
+ var printedHeader = false
+ opModeWatchers.forEachIndexed { _, op, modeChangedListenerSet ->
+ if (dumpOp >= 0 && dumpOp != op) {
+ return@forEachIndexed // continue
+ }
+ val opName = AppOpsManager.opToName(op)
+ var printedOpHeader = false
+ modeChangedListenerSet.forEachIndexed listenerLoop@ { listenerIndex, listener ->
+ with(printWriter) {
+ if (dumpPackage != null &&
+ dumpUid != UserHandle.getAppId(listener.watchingUid)) {
+ return@listenerLoop // continue
+ }
+ needSep = true
+ if (!printedHeader) {
+ println(" Op mode watchers:")
+ printedHeader = true
+ }
+ if (!printedOpHeader) {
+ print(" Op ")
+ print(opName)
+ println(":")
+ printedOpHeader = true
+ }
+ print(" #")
+ print(listenerIndex)
+ print(opName)
+ print(": ")
+ println(listener.toString())
+ }
+ }
+ }
+ }
+
+ if (packageModeWatchers.size > 0 && dumpOp < 0) {
+ var printedHeader = false
+ packageModeWatchers.forEachIndexed { _, packageName, listeners ->
+ with(printWriter) {
+ if (dumpPackage != null && dumpPackage != packageName) {
+ return@forEachIndexed // continue
+ }
+ needSep = true
+ if (!printedHeader) {
+ println(" Package mode watchers:")
+ printedHeader = true
+ }
+ print(" Pkg ")
+ print(packageName)
+ println(":")
+ listeners.forEachIndexed { listenerIndex, listener ->
+ print(" #")
+ print(listenerIndex)
+ print(": ")
+ println(listener.toString())
+ }
+ }
+ }
+ }
+ return needSep
}
companion object {
private val LOG_TAG = AppOpService::class.java.simpleName
+
+ // Constant meaning that any UID should be matched when dispatching callbacks
+ private const val UID_ANY = -2
+
+ // If watchedOpCode==ALL_OPS, notify for ops affected by the switch-op
+ private const val ALL_OPS = -2
}
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPersistence.kt b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPersistence.kt
index 031c9287a9a4..5faf96fecfd4 100644
--- a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPersistence.kt
@@ -19,7 +19,7 @@ package com.android.server.permission.access.appop
import android.util.Log
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
-import com.android.server.permission.access.UserState
+import com.android.server.permission.access.AccessState
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.util.attributeInt
import com.android.server.permission.access.util.attributeInterned
@@ -30,9 +30,9 @@ import com.android.server.permission.access.util.tag
import com.android.server.permission.access.util.tagName
abstract class BaseAppOpPersistence {
- abstract fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState)
+ abstract fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int)
- abstract fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState)
+ abstract fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int)
protected fun BinaryXmlPullParser.parseAppOps(appOpModes: IndexedMap<String, Int>) {
forEachTag {
@@ -67,7 +67,7 @@ abstract class BaseAppOpPersistence {
private const val TAG_APP_OP = "app-op"
- private const val ATTR_NAME = "name"
private const val ATTR_MODE = "mode"
+ private const val ATTR_NAME = "name"
}
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt
index 7f4e0f72537e..9c8c0ce3bde0 100644
--- a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt
@@ -18,9 +18,9 @@ package com.android.server.permission.access.appop
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
+import com.android.server.permission.access.AccessState
import com.android.server.permission.access.AppOpUri
import com.android.server.permission.access.SchemePolicy
-import com.android.server.permission.access.UserState
abstract class BaseAppOpPolicy(
private val persistence: BaseAppOpPersistence
@@ -28,11 +28,11 @@ abstract class BaseAppOpPolicy(
override val objectScheme: String
get() = AppOpUri.SCHEME
- override fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {
- with(persistence) { this@parseUserState.parseUserState(userId, userState) }
+ override fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
+ with(persistence) { this@parseUserState.parseUserState(state, userId) }
}
- override fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {
- with(persistence) { this@serializeUserState.serializeUserState(userId, userState) }
+ override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+ with(persistence) { this@serializeUserState.serializeUserState(state, userId) }
}
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPersistence.kt b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPersistence.kt
index 183a352b9acd..6ef117a44b2a 100644
--- a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPersistence.kt
@@ -19,6 +19,7 @@ package com.android.server.permission.access.appop
import android.util.Log
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
+import com.android.server.permission.access.AccessState
import com.android.server.permission.access.UserState
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.util.attributeInterned
@@ -28,20 +29,28 @@ import com.android.server.permission.access.util.tag
import com.android.server.permission.access.util.tagName
class PackageAppOpPersistence : BaseAppOpPersistence() {
- override fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {
+ override fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
when (tagName) {
- TAG_PACKAGE_APP_OPS -> parsePackageAppOps(userState)
+ TAG_PACKAGE_APP_OPS -> parsePackageAppOps(state, userId)
else -> {}
}
}
- private fun BinaryXmlPullParser.parsePackageAppOps(userState: UserState) {
+ private fun BinaryXmlPullParser.parsePackageAppOps(state: AccessState, userId: Int) {
+ val userState = state.userStates[userId]
forEachTag {
when (tagName) {
TAG_PACKAGE -> parsePackage(userState)
else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing app-op state")
}
}
+ userState.packageAppOpModes.retainAllIndexed { _, packageName, _ ->
+ val hasPackage = packageName in state.systemState.packageStates
+ if (!hasPackage) {
+ Log.w(LOG_TAG, "Dropping unknown package $packageName when parsing app-op state")
+ }
+ hasPackage
+ }
}
private fun BinaryXmlPullParser.parsePackage(userState: UserState) {
@@ -51,8 +60,8 @@ class PackageAppOpPersistence : BaseAppOpPersistence() {
parseAppOps(appOpModes)
}
- override fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {
- serializePackageAppOps(userState)
+ override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+ serializePackageAppOps(state.userStates[userId])
}
private fun BinaryXmlSerializer.serializePackageAppOps(userState: UserState) {
@@ -76,8 +85,8 @@ class PackageAppOpPersistence : BaseAppOpPersistence() {
companion object {
private val LOG_TAG = PackageAppOpPersistence::class.java.simpleName
- private const val TAG_PACKAGE_APP_OPS = "package-app-ops"
private const val TAG_PACKAGE = "package"
+ private const val TAG_PACKAGE_APP_OPS = "package-app-ops"
private const val ATTR_NAME = "name"
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt
index 607e5120fb37..af95fbdb2b6d 100644
--- a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt
@@ -56,8 +56,17 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) {
}
}
- fun MutateStateScope.removeAppOpModes(packageName: String, userId: Int): Boolean =
- newState.userStates[userId].packageAppOpModes.remove(packageName) != null
+ fun GetStateScope.getAppOpModes(packageName: String, userId: Int): IndexedMap<String, Int>? =
+ state.userStates[userId].packageAppOpModes[packageName]
+
+ fun MutateStateScope.removeAppOpModes(packageName: String, userId: Int): Boolean {
+ val userState = newState.userStates[userId]
+ val isChanged = userState.packageAppOpModes.remove(packageName) != null
+ if (isChanged) {
+ userState.requestWrite()
+ }
+ return isChanged
+ }
fun GetStateScope.getAppOpMode(packageName: String, userId: Int, appOpName: String): Int =
state.userStates[userId].packageAppOpModes[packageName]
diff --git a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPersistence.kt b/services/permission/java/com/android/server/permission/access/appop/UidAppOpPersistence.kt
index 3c3a9d18926a..7a965d470bb0 100644
--- a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/UidAppOpPersistence.kt
@@ -19,6 +19,7 @@ package com.android.server.permission.access.appop
import android.util.Log
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
+import com.android.server.permission.access.AccessState
import com.android.server.permission.access.UserState
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.util.attributeInt
@@ -28,44 +29,55 @@ import com.android.server.permission.access.util.tag
import com.android.server.permission.access.util.tagName
class UidAppOpPersistence : BaseAppOpPersistence() {
- override fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {
+ override fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
when (tagName) {
- TAG_UID_APP_OPS -> parseUidAppOps(userState)
+ TAG_UID_APP_OPS -> parseUidAppOps(state, userId)
else -> {}
}
}
- private fun BinaryXmlPullParser.parseUidAppOps(userState: UserState) {
+ private fun BinaryXmlPullParser.parseUidAppOps(state: AccessState, userId: Int) {
+ val userState = state.userStates[userId]
forEachTag {
when (tagName) {
- TAG_UID -> parseUid(userState)
+ TAG_APP_ID -> parseAppId(userState)
else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing app-op state")
}
}
+ userState.uidAppOpModes.retainAllIndexed { _, appId, _ ->
+ val hasAppId = appId in state.systemState.appIds
+ if (!hasAppId) {
+ Log.w(LOG_TAG, "Dropping unknown app ID $appId when parsing app-op state")
+ }
+ hasAppId
+ }
}
- private fun BinaryXmlPullParser.parseUid(userState: UserState) {
- val uid = getAttributeIntOrThrow(ATTR_UID)
+ private fun BinaryXmlPullParser.parseAppId(userState: UserState) {
+ val appId = getAttributeIntOrThrow(ATTR_ID)
val appOpModes = IndexedMap<String, Int>()
- userState.uidAppOpModes[uid] = appOpModes
+ userState.uidAppOpModes[appId] = appOpModes
parseAppOps(appOpModes)
}
- override fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {
- serializeUidAppOps(userState)
+ override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+ serializeUidAppOps(state.userStates[userId])
}
private fun BinaryXmlSerializer.serializeUidAppOps(userState: UserState) {
tag(TAG_UID_APP_OPS) {
- userState.uidAppOpModes.forEachIndexed { _, uid, appOpModes ->
- serializeUid(uid, appOpModes)
+ userState.uidAppOpModes.forEachIndexed { _, appId, appOpModes ->
+ serializeAppId(appId, appOpModes)
}
}
}
- private fun BinaryXmlSerializer.serializeUid(uid: Int, appOpModes: IndexedMap<String, Int>) {
- tag(TAG_UID) {
- attributeInt(ATTR_UID, uid)
+ private fun BinaryXmlSerializer.serializeAppId(
+ appId: Int,
+ appOpModes: IndexedMap<String, Int>
+ ) {
+ tag(TAG_APP_ID) {
+ attributeInt(ATTR_ID, appId)
serializeAppOps(appOpModes)
}
}
@@ -73,9 +85,9 @@ class UidAppOpPersistence : BaseAppOpPersistence() {
companion object {
private val LOG_TAG = UidAppOpPersistence::class.java.simpleName
+ private const val TAG_APP_ID = "app-id"
private const val TAG_UID_APP_OPS = "uid-app-ops"
- private const val TAG_UID = "uid"
- private const val ATTR_UID = "uid"
+ private const val ATTR_ID = "id"
}
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt
index 0b0103815e12..93b3a4484dff 100644
--- a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt
@@ -59,8 +59,14 @@ class UidAppOpPolicy : BaseAppOpPolicy(UidAppOpPersistence()) {
fun GetStateScope.getAppOpModes(appId: Int, userId: Int): IndexedMap<String, Int>? =
state.userStates[userId].uidAppOpModes[appId]
- fun MutateStateScope.removeAppOpModes(appId: Int, userId: Int): Boolean =
- newState.userStates[userId].uidAppOpModes.removeReturnOld(appId) != null
+ fun MutateStateScope.removeAppOpModes(appId: Int, userId: Int): Boolean {
+ val userState = newState.userStates[userId]
+ val isChanged = userState.uidAppOpModes.removeReturnOld(appId) != null
+ if (isChanged) {
+ userState.requestWrite()
+ }
+ return isChanged
+ }
fun GetStateScope.getAppOpMode(appId: Int, userId: Int, appOpName: String): Int =
state.userStates[userId].uidAppOpModes[appId]
diff --git a/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt b/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt
index 43f18e27f7aa..1e73be7923ae 100644
--- a/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt
+++ b/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt
@@ -148,6 +148,13 @@ inline fun <K, V> IndexedMap<K, V>.retainAllIndexed(predicate: (Int, K, V) -> Bo
return isChanged
}
+inline fun <K, V, R> IndexedMap<K, V>.mapNotNullIndexed(transform: (K, V) -> R?): IndexedList<R> =
+ IndexedList<R>().also { destination ->
+ forEachIndexed { _, key, value ->
+ transform(key, value)?.let { destination += it }
+ }
+ }
+
@Suppress("NOTHING_TO_INLINE")
inline operator fun <K, V> IndexedMap<K, V>.set(key: K, value: V) {
put(key, value)
diff --git a/services/permission/java/com/android/server/permission/access/permission/Permission.kt b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
index efa5bf3118d6..de2df74756b0 100644
--- a/services/permission/java/com/android/server/permission/access/permission/Permission.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
@@ -31,6 +31,9 @@ data class Permission(
inline val packageName: String
get() = permissionInfo.packageName
+ inline val groupName: String?
+ get() = permissionInfo.group
+
inline val isDynamic: Boolean
get() = type == TYPE_DYNAMIC
@@ -40,11 +43,17 @@ data class Permission(
inline val isRuntime: Boolean
get() = permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS
+ inline val isAppOp: Boolean
+ get() = permissionInfo.protection == PermissionInfo.PROTECTION_FLAG_APPOP
+
+ inline val isRemoved: Boolean
+ get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_REMOVED)
+
inline val isSoftRestricted: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED)
+ get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED)
inline val isHardRestricted: Boolean
- get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_HARD_RESTRICTED)
+ get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_HARD_RESTRICTED)
inline val isSignature: Boolean
get() = permissionInfo.protection == PermissionInfo.PROTECTION_SIGNATURE
@@ -109,6 +118,9 @@ data class Permission(
inline val isKnownSigner: Boolean
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER)
+ inline val hasGids: Boolean
+ get() = throw NotImplementedError()
+
inline val protectionLevel: Int
@Suppress("DEPRECATION")
get() = permissionInfo.protectionLevel
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index 6b2b1856f7fe..a4708c85345f 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -313,6 +313,11 @@ object PermissionFlags {
APP_OP_REVOKED or ONE_TIME or HIBERNATION or USER_SELECTED
/**
+ * Mask for all permission flags about permission exemption.
+ */
+ const val MASK_EXEMPT = INSTALLER_EXEMPT or SYSTEM_EXEMPT or UPGRADE_EXEMPT
+
+ /**
* Mask for all API permission flags about permission restriction.
*/
private const val API_MASK_RESTRICTION =
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 82017362da29..a70468e2400d 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -16,6 +16,11 @@
package com.android.server.permission.access.permission
+import android.Manifest
+import android.app.ActivityManager
+import android.compat.annotation.ChangeId
+import android.compat.annotation.EnabledAfter
+import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManagerInternal
import android.content.pm.PermissionGroupInfo
@@ -23,17 +28,32 @@ import android.content.pm.PermissionInfo
import android.content.pm.permission.SplitPermissionInfoParcelable
import android.os.Binder
import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.os.Message
import android.os.Process
+import android.os.RemoteCallbackList
+import android.os.RemoteException
+import android.os.ServiceManager
import android.os.UserHandle
import android.permission.IOnPermissionsChangeListener
+import android.permission.PermissionManager
+import android.provider.Settings
+import android.util.Log
+import com.android.internal.compat.IPlatformCompat
+import com.android.server.FgThread
import com.android.server.LocalManagerRegistry
import com.android.server.LocalServices
+import com.android.server.ServiceThread
+import com.android.server.SystemConfig
import com.android.server.pm.PackageManagerLocal
import com.android.server.pm.permission.PermissionManagerServiceInterface
import com.android.server.permission.access.AccessCheckingService
import com.android.server.permission.access.PermissionUri
import com.android.server.permission.access.UidUri
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.util.hasAnyBit
import com.android.server.permission.access.util.hasBits
import com.android.server.pm.UserManagerService
import com.android.server.pm.permission.LegacyPermission
@@ -53,21 +73,56 @@ class PermissionService(
private val policy =
service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as UidPermissionPolicy
+ private val context = service.context
private lateinit var packageManagerInternal: PackageManagerInternal
private lateinit var packageManagerLocal: PackageManagerLocal
+ private lateinit var platformCompat: IPlatformCompat
private lateinit var userManagerService: UserManagerService
private val mountedStorageVolumes = IndexedSet<String?>()
+ private lateinit var handlerThread: HandlerThread
+ private lateinit var handler: Handler
+
+ private lateinit var onPermissionsChangeListeners: OnPermissionsChangeListeners
+ private lateinit var permissionFlagsListener: OnPermissionFlagsChangedListener
+
fun initialize() {
packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java)
packageManagerLocal =
LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java)
+ platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)
+ )
userManagerService = UserManagerService.getInstance()
+
+ handlerThread = ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true)
+ handler = Handler(handlerThread.looper)
+
+ onPermissionsChangeListeners = OnPermissionsChangeListeners(FgThread.get().looper)
+ permissionFlagsListener = OnPermissionFlagsChangedListener()
+ policy.addOnPermissionFlagsChangedListener(permissionFlagsListener)
}
override fun getAllPermissionGroups(flags: Int): List<PermissionGroupInfo> {
- TODO("Not yet implemented")
+ val callingUid = Binder.getCallingUid()
+ packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+ if (snapshot.isUidInstantApp(callingUid)) {
+ return emptyList()
+ }
+
+ val permissionGroups = service.getState {
+ with(policy) { getPermissionGroups() }
+ }
+
+ return permissionGroups.mapNotNullIndexed { _, permissionGroup ->
+ if (snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
+ permissionGroup.generatePermissionGroupInfo(flags)
+ } else {
+ null
+ }
+ }
+ }
}
override fun getPermissionGroupInfo(
@@ -82,7 +137,7 @@ class PermissionService(
}
permissionGroup = service.getState {
- with(policy) { getPermissionGroup(permissionGroupName) }
+ with(policy) { getPermissionGroups()[permissionGroupName] }
} ?: return null
val isPermissionGroupVisible =
@@ -120,7 +175,7 @@ class PermissionService(
}
permission = service.getState {
- with(policy) { getPermission(permissionName) }
+ with(policy) { getPermissions()[permissionName] }
} ?: return null
val isPermissionVisible =
@@ -148,7 +203,7 @@ class PermissionService(
*/
private fun Permission.generatePermissionInfo(
flags: Int,
- targetSdkVersion: Int
+ targetSdkVersion: Int = Build.VERSION_CODES.CUR_DEVELOPMENT
): PermissionInfo =
@Suppress("DEPRECATION")
PermissionInfo(permissionInfo).apply {
@@ -165,10 +220,40 @@ class PermissionService(
}
override fun queryPermissionsByGroup(
- permissionGroupName: String,
+ permissionGroupName: String?,
flags: Int
- ): List<PermissionInfo> {
- TODO("Not yet implemented")
+ ): List<PermissionInfo>? {
+ val callingUid = Binder.getCallingUid()
+ packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+ if (snapshot.isUidInstantApp(callingUid)) {
+ return null
+ }
+
+ if (permissionGroupName != null) {
+ val permissionGroup = service.getState {
+ with(policy) { getPermissionGroups()[permissionGroupName] }
+ } ?: return null
+
+ if (!snapshot.isPackageVisibleToUid(
+ permissionGroup.packageName, callingUid
+ )) {
+ return null
+ }
+ }
+
+ val permissions = service.getState {
+ with(policy) { getPermissions() }
+ }
+
+ return permissions.mapNotNullIndexed { _, permission ->
+ if (permission.groupName == permissionGroupName &&
+ snapshot.isPackageVisibleToUid(permission.packageName, callingUid)) {
+ permission.generatePermissionInfo(flags)
+ } else {
+ null
+ }
+ }
+ }
}
override fun getAllPermissionsWithProtection(protection: Int): List<PermissionInfo> {
@@ -224,11 +309,11 @@ class PermissionService(
}
override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
- TODO("Not yet implemented")
+ onPermissionsChangeListeners.addListener(listener)
}
override fun removeOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
- TODO("Not yet implemented")
+ onPermissionsChangeListeners.removeListener(listener)
}
override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int {
@@ -245,16 +330,49 @@ class PermissionService(
permissionName: String,
userId: Int
): Boolean {
- TODO("Not yet implemented")
+ if (UserHandle.getCallingUserId() != userId) {
+ context.enforceCallingPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "isPermissionRevokedByPolicy for user $userId"
+ )
+ }
+
+ if (checkPermission(packageName, permissionName, userId) ==
+ PackageManager.PERMISSION_GRANTED) {
+ return false
+ }
+
+ val callingUid = Binder.getCallingUid()
+ if (packageManagerLocal.withUnfilteredSnapshot()
+ .use { !it.isPackageVisibleToUid(packageName, userId, callingUid) }) {
+ return false
+ }
+
+ val permissionFlags = getPermissionFlagsUnchecked(packageName,
+ permissionName, callingUid, userId)
+ return permissionFlags.hasBits(PackageManager.FLAG_PERMISSION_POLICY_FIXED)
+ }
+
+ private fun getPermissionFlagsUnchecked(
+ packageName: String,
+ permName: String,
+ callingUid: Int,
+ userId: Int
+ ): Int {
+ throw NotImplementedError()
}
override fun isPermissionsReviewRequired(packageName: String, userId: Int): Boolean {
+ requireNotNull(packageName) { "packageName" }
+ // TODO(b/173235285): Some caller may pass USER_ALL as userId.
+ // Preconditions.checkArgumentNonnegative(userId, "userId");
val packageState = packageManagerLocal.withUnfilteredSnapshot()
.use { it.packageStates[packageName] } ?: return false
val permissionFlags = service.getState {
with(policy) { getUidPermissionFlags(packageState.appId, userId) }
} ?: return false
- return permissionFlags.anyIndexed { _, _, flags -> PermissionFlags.isReviewRequired(flags) }
+ return permissionFlags.anyIndexed { _, _, flags -> PermissionFlags.isReviewRequired(flags)
+ }
}
override fun shouldShowRequestPermissionRationale(
@@ -262,7 +380,70 @@ class PermissionService(
permissionName: String,
userId: Int
): Boolean {
- TODO("Not yet implemented")
+ val callingUid = Binder.getCallingUid()
+ if (UserHandle.getCallingUserId() != userId) {
+ context.enforceCallingPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "canShowRequestPermissionRationale for user $userId"
+ )
+ }
+
+ val appId = packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+ snapshot.packageStates[packageName]?.appId ?: return false
+ }
+ if (UserHandle.getAppId(callingUid) != appId) {
+ return false
+ }
+
+ if (checkPermission(packageName, permissionName, userId) ==
+ PackageManager.PERMISSION_GRANTED) {
+ return false
+ }
+
+ val identity = Binder.clearCallingIdentity()
+ val permissionFlags = try {
+ getPermissionFlagsInternal(packageName, permissionName, callingUid, userId)
+ } finally {
+ Binder.restoreCallingIdentity(identity)
+ }
+
+ val fixedFlags = (PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED
+ or PermissionFlags.USER_FIXED)
+
+ if (permissionFlags.hasAnyBit(fixedFlags) ||
+ permissionFlags.hasBits(PermissionFlags.RESTRICTION_REVOKED)) {
+ return false
+ }
+
+ val token = Binder.clearCallingIdentity()
+ try {
+ if (permissionName == Manifest.permission.ACCESS_BACKGROUND_LOCATION &&
+ platformCompat.isChangeEnabledByPackageName(
+ BACKGROUND_RATIONALE_CHANGE_ID, packageName, userId)
+ ) {
+ return true
+ }
+ } catch (e: RemoteException) {
+ Log.e(LOG_TAG, "Unable to check if compatibility change is enabled.", e)
+ } finally {
+ Binder.restoreCallingIdentity(token)
+ }
+
+ return permissionFlags and PackageManager.FLAG_PERMISSION_USER_SET != 0
+ }
+
+ /**
+ * read internal permission flags
+ * @return internal permission Flags
+ * @see PermissionFlags
+ */
+ private fun getPermissionFlagsInternal(
+ packageName: String,
+ permName: String,
+ callingUid: Int,
+ userId: Int
+ ): Int {
+ throw NotImplementedError()
}
override fun updatePermissionFlags(
@@ -327,7 +508,9 @@ class PermissionService(
}
override fun getSplitPermissions(): List<SplitPermissionInfoParcelable> {
- TODO("Not yet implemented")
+ return PermissionManager.splitPermissionInfoListToParcelableList(
+ SystemConfig.getInstance().splitPermissions
+ )
}
override fun getAppOpPermissionPackages(permissionName: String): Array<String> {
@@ -335,7 +518,22 @@ class PermissionService(
}
override fun getAllAppOpPermissionPackages(): Map<String, Set<String>> {
- TODO("Not yet implemented")
+ val appOpPermissionPackageNames = IndexedMap<String, IndexedSet<String>>()
+ val permissions = service.getState { with(policy) { getPermissions() } }
+ packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+ snapshot.packageStates.forEach packageStates@{ (_, packageState) ->
+ val androidPackage = packageState.androidPackage ?: return@packageStates
+ androidPackage.requestedPermissions.forEach requestedPermissions@{ permissionName ->
+ val permission = permissions[permissionName] ?: return@requestedPermissions
+ if (permission.isAppOp) {
+ val packageNames = appOpPermissionPackageNames
+ .getOrPut(permissionName) { IndexedSet() }
+ packageNames += androidPackage.packageName
+ }
+ }
+ }
+ }
+ return appOpPermissionPackageNames
}
override fun getGidsForUid(uid: Int): IntArray {
@@ -468,6 +666,28 @@ class PermissionService(
}
}
+
+ /**
+ * This method should typically only be used when granting or revoking permissions, since the
+ * app may immediately restart after this call.
+ *
+ * If you're doing surgery on app code/data, use [PackageFreezer] to guard your work against
+ * the app being relaunched.
+ */
+ private fun killUid(appId: Int, userId: Int, reason: String) {
+ val activityManager = ActivityManager.getService()
+ if (activityManager != null) {
+ val identity = Binder.clearCallingIdentity()
+ try {
+ activityManager.killUidForPermissionChange(appId, userId, reason)
+ } catch (e: RemoteException) {
+ /* ignore - same process */
+ } finally {
+ Binder.restoreCallingIdentity(identity)
+ }
+ }
+ }
+
/**
* Check whether a UID belongs to an instant app.
*/
@@ -504,4 +724,133 @@ class PermissionService(
val user = UserHandle.of(userId)
return filtered(uid, user).use { it.getPackageState(packageName) != null }
}
+
+ /**
+ * Callback invoked when interesting actions have been taken on a permission.
+ */
+ private inner class OnPermissionFlagsChangedListener :
+ UidPermissionPolicy.OnPermissionFlagsChangedListener {
+ override fun onPermissionFlagsChanged(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ oldFlags: Int,
+ newFlags: Int
+ ) {
+ val uid = UserHandle.getUid(userId, appId)
+ val permission = service.getState {
+ with(policy) { getPermissions()[permissionName] }
+ } ?: return
+
+ val isPermissionGranted = !PermissionFlags.isPermissionGranted(oldFlags) &&
+ PermissionFlags.isPermissionGranted(newFlags)
+ val isPermissionRevoked = PermissionFlags.isPermissionGranted(oldFlags) &&
+ !PermissionFlags.isPermissionGranted(newFlags)
+
+ if (isPermissionGranted) {
+ if (permission.isRuntime) {
+ onPermissionsChangeListeners.onPermissionsChanged(uid)
+ }
+ handler.post {
+ if (permission.hasGids) {
+ killUid(appId, userId, PermissionManager.KILL_APP_REASON_GIDS_CHANGED)
+ }
+ }
+ } else if (isPermissionRevoked) {
+ // TODO: STOPSHIP skip kill for revokePostNotificationPermissionWithoutKillForTest
+ if (permission.isRuntime) {
+ onPermissionsChangeListeners.onPermissionsChanged(uid)
+ handler.post {
+ if (!(permissionName == Manifest.permission.POST_NOTIFICATIONS &&
+ isAppBackupAndRestoreRunning(uid))) {
+ killUid(
+ appId, userId, PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED
+ )
+ }
+ }
+ }
+ } else if (oldFlags != newFlags) {
+ onPermissionsChangeListeners.onPermissionsChanged(uid)
+ }
+ }
+
+ private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
+ if (checkUidPermission(uid, Manifest.permission.BACKUP) !=
+ PackageManager.PERMISSION_GRANTED) {
+ return false
+ }
+ return try {
+ val userId = UserHandle.getUserId(uid)
+ val isInSetup = Settings.Secure.getIntForUser(
+ context.contentResolver, Settings.Secure.USER_SETUP_COMPLETE, userId
+ ) == 0
+ val isInDeferredSetup = Settings.Secure.getIntForUser(
+ context.contentResolver,
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId
+ ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
+ isInSetup || isInDeferredSetup
+ } catch (e: Settings.SettingNotFoundException) {
+ Log.w(LOG_TAG, "Failed to check if the user is in restore: $e")
+ false
+ }
+ }
+ }
+
+ private class OnPermissionsChangeListeners(looper: Looper) : Handler(looper) {
+ private val listeners = RemoteCallbackList<IOnPermissionsChangeListener>()
+
+ override fun handleMessage(msg: Message) {
+ when (msg.what) {
+ MSG_ON_PERMISSIONS_CHANGED -> {
+ val uid = msg.arg1
+ handleOnPermissionsChanged(uid)
+ }
+ }
+ }
+
+ private fun handleOnPermissionsChanged(uid: Int) {
+ val count = listeners.beginBroadcast()
+ try {
+ for (i in 0 until count) {
+ val callback = listeners.getBroadcastItem(i)
+ try {
+ callback.onPermissionsChanged(uid)
+ } catch (e: RemoteException) {
+ Log.e(LOG_TAG, "Permission listener is dead", e)
+ }
+ }
+ } finally {
+ listeners.finishBroadcast()
+ }
+ }
+
+ fun addListener(listener: IOnPermissionsChangeListener) {
+ listeners.register(listener)
+ }
+
+ fun removeListener(listener: IOnPermissionsChangeListener) {
+ listeners.unregister(listener)
+ }
+
+ fun onPermissionsChanged(uid: Int) {
+ if (listeners.registeredCallbackCount > 0) {
+ obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0).sendToTarget()
+ }
+ }
+ companion object {
+ private const val MSG_ON_PERMISSIONS_CHANGED = 1
+ }
+ }
+
+ companion object {
+ private val LOG_TAG = PermissionService::class.java.simpleName
+
+ /**
+ * This change makes it so that apps are told to show rationale for asking for background
+ * location access every time they request.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private val BACKGROUND_RATIONALE_CHANGE_ID = 147316723L
+ }
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt
index 061933a13fb4..35cdbce01128 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt
@@ -20,7 +20,8 @@ import android.content.pm.PermissionInfo
import android.util.Log
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
-import com.android.server.permission.access.SystemState
+import com.android.server.permission.access.AccessState
+import com.android.server.permission.access.UserState
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.util.attribute
import com.android.server.permission.access.util.attributeInt
@@ -37,7 +38,8 @@ import com.android.server.permission.access.util.tag
import com.android.server.permission.access.util.tagName
class UidPermissionPersistence {
- fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) {
+ fun BinaryXmlPullParser.parseSystemState(state: AccessState) {
+ val systemState = state.systemState
when (tagName) {
TAG_PERMISSION_TREES -> parsePermissions(systemState.permissionTrees)
TAG_PERMISSIONS -> parsePermissions(systemState.permissions)
@@ -84,7 +86,8 @@ class UidPermissionPersistence {
permissions[name] = permission
}
- fun BinaryXmlSerializer.serializeSystemState(systemState: SystemState) {
+ fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {
+ val systemState = state.systemState
serializePermissions(TAG_PERMISSION_TREES, systemState.permissionTrees)
serializePermissions(TAG_PERMISSIONS, systemState.permissions)
}
@@ -121,14 +124,102 @@ class UidPermissionPersistence {
}
}
+ fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
+ when (tagName) {
+ TAG_PERMISSIONS -> parsePermissionFlags(state, userId)
+ else -> {}
+ }
+ }
+
+ private fun BinaryXmlPullParser.parsePermissionFlags(state: AccessState, userId: Int) {
+ val userState = state.userStates[userId]
+ forEachTag {
+ when (tagName) {
+ TAG_APP_ID -> parseAppId(userState)
+ else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+ }
+ }
+ userState.uidPermissionFlags.retainAllIndexed { _, appId, _ ->
+ val hasAppId = appId in state.systemState.appIds
+ if (!hasAppId) {
+ Log.w(LOG_TAG, "Dropping unknown app ID $appId when parsing permission state")
+ }
+ hasAppId
+ }
+ }
+
+ private fun BinaryXmlPullParser.parseAppId(userState: UserState) {
+ val appId = getAttributeIntOrThrow(ATTR_ID)
+ val permissionFlags = IndexedMap<String, Int>()
+ userState.uidPermissionFlags[appId] = permissionFlags
+ parseAppIdPermissions(permissionFlags)
+ }
+
+ private fun BinaryXmlPullParser.parseAppIdPermissions(
+ permissionFlags: IndexedMap<String, Int>
+ ) {
+ forEachTag {
+ when (tagName) {
+ TAG_PERMISSION -> parseAppIdPermission(permissionFlags)
+ else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+ }
+ }
+ }
+
+ private fun BinaryXmlPullParser.parseAppIdPermission(permissionFlags: IndexedMap<String, Int>) {
+ val name = getAttributeValueOrThrow(ATTR_NAME).intern()
+ val flags = getAttributeIntOrThrow(ATTR_FLAGS)
+ permissionFlags[name] = flags
+ }
+
+ fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+ serializePermissionFlags(state.userStates[userId])
+ }
+
+ private fun BinaryXmlSerializer.serializePermissionFlags(userState: UserState) {
+ tag(TAG_PERMISSIONS) {
+ userState.uidPermissionFlags.forEachIndexed { _, appId, permissionFlags ->
+ serializeAppId(appId, permissionFlags)
+ }
+ }
+ }
+
+ private fun BinaryXmlSerializer.serializeAppId(
+ appId: Int,
+ permissionFlags: IndexedMap<String, Int>
+ ) {
+ tag(TAG_APP_ID) {
+ attributeInt(ATTR_ID, appId)
+ serializeAppIdPermissions(permissionFlags)
+ }
+ }
+
+ private fun BinaryXmlSerializer.serializeAppIdPermissions(
+ permissionFlags: IndexedMap<String, Int>
+ ) {
+ permissionFlags.forEachIndexed { _, name, flags ->
+ serializeAppIdPermission(name, flags)
+ }
+ }
+
+ private fun BinaryXmlSerializer.serializeAppIdPermission(name: String, flags: Int) {
+ tag(TAG_PERMISSION) {
+ attributeInterned(ATTR_NAME, name)
+ attributeInt(ATTR_FLAGS, flags)
+ }
+ }
+
companion object {
private val LOG_TAG = UidPermissionPersistence::class.java.simpleName
+ private const val TAG_APP_ID = "app-id"
private const val TAG_PERMISSION = "permission"
- private const val TAG_PERMISSION_TREES = "permission-trees"
private const val TAG_PERMISSIONS = "permissions"
+ private const val TAG_PERMISSION_TREES = "permission-trees"
+ private const val ATTR_FLAGS = "flags"
private const val ATTR_ICON = "icon"
+ private const val ATTR_ID = "id"
private const val ATTR_LABEL = "label"
private const val ATTR_NAME = "name"
private const val ATTR_PACKAGE_NAME = "packageName"
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
index b2f52cc814cb..e6636e4e78af 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
@@ -33,10 +33,10 @@ import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.PermissionUri
import com.android.server.permission.access.SchemePolicy
-import com.android.server.permission.access.SystemState
import com.android.server.permission.access.UidUri
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.util.andInv
+import com.android.server.permission.access.util.hasAnyBit
import com.android.server.permission.access.util.hasBits
import com.android.server.pm.KnownPackages
import com.android.server.pm.parsing.PackageInfoUtils
@@ -77,7 +77,9 @@ class UidPermissionPolicy : SchemePolicy() {
override fun MutateStateScope.onUserAdded(userId: Int) {
newState.systemState.packageStates.forEach { (_, packageState) ->
evaluateAllPermissionStatesForPackageAndUser(packageState, userId, null)
- grantImplicitPermissions(packageState, userId)
+ }
+ newState.systemState.appIds.forEachKeyIndexed { _, appId ->
+ inheritImplicitPermissionStates(appId, userId)
}
}
@@ -89,6 +91,47 @@ class UidPermissionPolicy : SchemePolicy() {
}
}
+ override fun MutateStateScope.onStorageVolumeMounted(
+ volumeUuid: String?,
+ isSystemUpdated: Boolean
+ ) {
+ // TODO: STOPSHIP: Either make addPermissionGroups() favor system packages
+ // like addPermissions(), or sort system packages before non-system packages for this loop.
+ val changedPermissionNames = IndexedSet<String>()
+ newState.systemState.packageStates.forEach { (_, packageState) ->
+ val androidPackage = packageState.androidPackage
+ if (androidPackage == null || androidPackage.volumeUuid != volumeUuid) {
+ return@forEach
+ }
+ adoptPermissions(packageState, changedPermissionNames)
+ addPermissionGroups(packageState)
+ addPermissions(packageState, changedPermissionNames)
+ trimPermissions(packageState.packageName, changedPermissionNames)
+ trimPermissionStates(packageState.appId)
+ }
+ changedPermissionNames.forEachIndexed { _, permissionName ->
+ evaluatePermissionStateForAllPackages(permissionName, null)
+ }
+
+ newState.systemState.packageStates.forEach { (_, packageState) ->
+ val androidPackage = packageState.androidPackage
+ if (androidPackage == null || androidPackage.volumeUuid != volumeUuid) {
+ return@forEach
+ }
+ val installedPackageState = if (isSystemUpdated) packageState else null
+ evaluateAllPermissionStatesForPackage(packageState, installedPackageState)
+ }
+ newState.systemState.packageStates.forEach { (_, packageState) ->
+ val androidPackage = packageState.androidPackage
+ if (androidPackage == null || androidPackage.volumeUuid != volumeUuid) {
+ return@forEach
+ }
+ newState.systemState.userIds.forEachIndexed { _, userId ->
+ inheritImplicitPermissionStates(packageState.appId, userId)
+ }
+ }
+ }
+
override fun MutateStateScope.onPackageAdded(packageState: PackageState) {
val changedPermissionNames = IndexedSet<String>()
adoptPermissions(packageState, changedPermissionNames)
@@ -96,17 +139,21 @@ class UidPermissionPolicy : SchemePolicy() {
addPermissions(packageState, changedPermissionNames)
// TODO: revokeStoragePermissionsIfScopeExpandedInternal()
trimPermissions(packageState.packageName, changedPermissionNames)
+ trimPermissionStates(packageState.appId)
changedPermissionNames.forEachIndexed { _, permissionName ->
- evaluatePermissionStateForAllPackages(permissionName, packageState)
+ evaluatePermissionStateForAllPackages(permissionName, null)
}
-
evaluateAllPermissionStatesForPackage(packageState, packageState)
newState.systemState.userIds.forEachIndexed { _, userId ->
- grantImplicitPermissions(packageState, userId)
+ inheritImplicitPermissionStates(packageState.appId, userId)
}
+ }
- // TODO: add trimPermissionStates() here for removing the permission states that are
- // no longer requested. (equivalent to revokeUnusedSharedUserPermissionsLocked())
+ override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
+ // TODO: STOPSHIP: Remove this check or at least turn into logging.
+ check(packageName !in newState.systemState.disabledSystemPackageStates) {
+ "Package $packageName reported as removed before disabled system package is enabled"
+ }
}
private fun MutateStateScope.adoptPermissions(
@@ -362,6 +409,20 @@ class UidPermissionPolicy : SchemePolicy() {
}
}
+ private fun MutateStateScope.trimPermissionStates(appId: Int) {
+ val requestedPermissions = IndexedSet<String>()
+ forEachPackageInAppId(appId) {
+ requestedPermissions += it.androidPackage!!.requestedPermissions
+ }
+ newState.userStates.forEachIndexed { _, userId, userState ->
+ userState.uidPermissionFlags[appId].forEachReversedIndexed { _, permissionName, _ ->
+ if (permissionName !in requestedPermissions) {
+ setPermissionFlags(appId, userId, permissionName, 0)
+ }
+ }
+ }
+ }
+
private fun MutateStateScope.evaluatePermissionStateForAllPackages(
permissionName: String,
installedPackageState: PackageState?
@@ -416,8 +477,17 @@ class UidPermissionPolicy : SchemePolicy() {
// For non-shared-user packages with missing androidPackage, skip evaluation.
return
}
- val permission = newState.systemState.permissions[permissionName] ?: return
+ val permission = newState.systemState.permissions[permissionName]
val oldFlags = getPermissionFlags(appId, userId, permissionName)
+ if (permission == null) {
+ if (oldFlags == 0) {
+ // If the permission definition is missing and we don't have any permission states
+ // for this permission, add the INSTALL_REVOKED flag to ensure that we don't
+ // automatically grant the permission when it's defined
+ setPermissionFlags(appId, userId, permissionName, PermissionFlags.INSTALL_REVOKED)
+ }
+ return
+ }
if (permission.isNormal) {
val wasGranted = oldFlags.hasBits(PermissionFlags.INSTALL_GRANTED)
if (!wasGranted) {
@@ -480,21 +550,98 @@ class UidPermissionPolicy : SchemePolicy() {
}
setPermissionFlags(appId, userId, permissionName, newFlags)
} else if (permission.isRuntime) {
- // TODO: add runtime permissions
+ var newFlags = oldFlags and PermissionFlags.MASK_RUNTIME
+ if (getAppIdTargetSdkVersion(appId, permissionName) < Build.VERSION_CODES.M) {
+ newFlags = newFlags or PermissionFlags.LEGACY_GRANTED
+ // Explicitly check against the old state to determine if this permission is new.
+ val isNewPermission =
+ getOldStatePermissionFlags(appId, userId, permissionName) == 0
+ if (isNewPermission) {
+ newFlags = newFlags or PermissionFlags.IMPLICIT
+ }
+ } else {
+ newFlags = newFlags andInv PermissionFlags.LEGACY_GRANTED
+ val wasGrantedByImplicit = newFlags.hasBits(PermissionFlags.IMPLICIT_GRANTED)
+ val isLeanBackNotificationsPermission = newState.systemState.isLeanback &&
+ permissionName in NOTIFICATION_PERMISSIONS
+ val isImplicitPermission = anyPackageInAppId(appId) {
+ permissionName in it.androidPackage!!.implicitPermissions
+ }
+ val sourcePermissions = newState.systemState
+ .implicitToSourcePermissions[permissionName]
+ val isAnySourcePermissionNonRuntime = sourcePermissions?.any {
+ val sourcePermission = newState.systemState.permissions[it]
+ checkNotNull(sourcePermission) {
+ "Unknown source permission $it in split permissions"
+ }
+ !sourcePermission.isRuntime
+ } ?: false
+ val shouldGrantByImplicit = isLeanBackNotificationsPermission ||
+ (isImplicitPermission && isAnySourcePermissionNonRuntime)
+ if (shouldGrantByImplicit) {
+ newFlags = newFlags or PermissionFlags.IMPLICIT_GRANTED
+ } else {
+ newFlags = newFlags andInv PermissionFlags.IMPLICIT_GRANTED
+ }
+ val hasImplicitFlag = newFlags.hasBits(PermissionFlags.IMPLICIT)
+ if (!isImplicitPermission && hasImplicitFlag) {
+ // TODO: We might not want to remove the IMPLICIT flag
+ // for NOTIFICATION_PERMISSIONS
+ newFlags = newFlags andInv PermissionFlags.IMPLICIT
+ var shouldRetainAsNearbyDevices = false
+ if (permissionName in NEARBY_DEVICES_PERMISSIONS) {
+ val accessBackgroundLocationFlags = getPermissionFlags(
+ appId, userId, Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ )
+ shouldRetainAsNearbyDevices =
+ PermissionFlags.isAppOpGranted(accessBackgroundLocationFlags) &&
+ !accessBackgroundLocationFlags.hasBits(PermissionFlags.IMPLICIT)
+ }
+ // These are the permission flags that imply we shouldn't automatically
+ // modify the permission grant state.
+ val shouldRetainByMask = newFlags.hasAnyBit(
+ PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED
+ )
+ if (shouldRetainAsNearbyDevices || shouldRetainByMask) {
+ if (wasGrantedByImplicit) {
+ newFlags = newFlags or PermissionFlags.RUNTIME_GRANTED
+ }
+ } else {
+ newFlags = newFlags andInv (
+ PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET or
+ PermissionFlags.USER_FIXED
+ )
+ }
+ }
+ }
+
+ val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
+ val isHardRestricted = permission.isHardRestricted && !isExempt
+ newFlags = if (isHardRestricted) {
+ newFlags or PermissionFlags.RESTRICTION_REVOKED
+ } else {
+ newFlags andInv PermissionFlags.RESTRICTION_REVOKED
+ }
+ val isSoftRestricted = permission.isSoftRestricted && !isExempt
+ newFlags = if (isSoftRestricted) {
+ newFlags or PermissionFlags.SOFT_RESTRICTED
+ } else {
+ newFlags andInv PermissionFlags.SOFT_RESTRICTED
+ }
+ setPermissionFlags(appId, userId, permissionName, newFlags)
} else {
Log.e(LOG_TAG, "Unknown protection level ${permission.protectionLevel}" +
"for permission ${permission.name} while evaluating permission state" +
"for appId $appId and userId $userId")
}
-
- // TODO: revokePermissionsNoLongerImplicitLocked() for runtime permissions
}
- private fun MutateStateScope.grantImplicitPermissions(packageState: PackageState, userId: Int) {
- val appId = packageState.appId
- val androidPackage = packageState.androidPackage ?: return
- androidPackage.implicitPermissions.forEachIndexed implicitPermissions@ {
- _, implicitPermissionName ->
+ private fun MutateStateScope.inheritImplicitPermissionStates(appId: Int, userId: Int) {
+ val implicitPermissions = IndexedSet<String>()
+ forEachPackageInAppId(appId) {
+ implicitPermissions += it.androidPackage!!.implicitPermissions
+ }
+ implicitPermissions.forEachIndexed implicitPermissions@ { _, implicitPermissionName ->
val implicitPermission = newState.systemState.permissions[implicitPermissionName]
checkNotNull(implicitPermission) {
"Unknown implicit permission $implicitPermissionName in split permissions"
@@ -503,15 +650,14 @@ class UidPermissionPolicy : SchemePolicy() {
return@implicitPermissions
}
// Explicitly check against the old state to determine if this permission is new.
- val isNewPermission = getPermissionFlags(
- appId, userId, implicitPermissionName, oldState
- ) == 0
+ val isNewPermission =
+ getOldStatePermissionFlags(appId, userId, implicitPermissionName) == 0
if (!isNewPermission) {
return@implicitPermissions
}
val sourcePermissions = newState.systemState
.implicitToSourcePermissions[implicitPermissionName] ?: return@implicitPermissions
- var newFlags = 0
+ var newFlags = getPermissionFlags(appId, userId, implicitPermissionName)
sourcePermissions.forEachIndexed sourcePermissions@ { _, sourcePermissionName ->
val sourcePermission = newState.systemState.permissions[sourcePermissionName]
checkNotNull(sourcePermission) {
@@ -526,12 +672,13 @@ class UidPermissionPolicy : SchemePolicy() {
newFlags = 0
}
newFlags = newFlags or (sourceFlags and PermissionFlags.MASK_RUNTIME)
- if (!sourcePermission.isRuntime && isSourceGranted) {
- newFlags = newFlags or PermissionFlags.IMPLICIT_GRANTED
- }
}
}
- newFlags = newFlags or PermissionFlags.IMPLICIT
+ if (implicitPermissionName in RETAIN_IMPLICIT_FLAGS_PERMISSIONS) {
+ newFlags = newFlags andInv PermissionFlags.IMPLICIT
+ } else {
+ newFlags = newFlags or PermissionFlags.IMPLICIT
+ }
setPermissionFlags(appId, userId, implicitPermissionName, newFlags)
}
}
@@ -619,7 +766,7 @@ class UidPermissionPolicy : SchemePolicy() {
val permissionAllowlist = newState.systemState.permissionAllowlist
// TODO(b/261913353): STOPSHIP: Add AndroidPackage.apexModuleName. The below is only for
// passing compilation but won't actually work.
- //val apexModuleName = androidPackage.apexModuleName
+ // val apexModuleName = androidPackage.apexModuleName
val apexModuleName = androidPackage.packageName
val packageName = androidPackage.packageName
return when {
@@ -655,6 +802,17 @@ class UidPermissionPolicy : SchemePolicy() {
}
}
+ private fun MutateStateScope.getAppIdTargetSdkVersion(appId: Int, permissionName: String): Int {
+ var targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT
+ forEachPackageInAppId(appId) { packageState ->
+ val androidPackage = packageState.androidPackage!!
+ if (permissionName in androidPackage.requestedPermissions) {
+ targetSdkVersion = targetSdkVersion.coerceAtMost(androidPackage.targetSdkVersion)
+ }
+ }
+ return targetSdkVersion
+ }
+
private fun MutateStateScope.anyPackageInAppId(
appId: Int,
state: AccessState = newState,
@@ -667,6 +825,20 @@ class UidPermissionPolicy : SchemePolicy() {
}
}
+ private fun MutateStateScope.forEachPackageInAppId(
+ appId: Int,
+ state: AccessState = newState,
+ action: (PackageState) -> Unit
+ ) {
+ val packageNames = state.systemState.appIds[appId]
+ packageNames.forEachIndexed { _, packageName ->
+ val packageState = state.systemState.packageStates[packageName]!!
+ if (packageState.androidPackage != null) {
+ action(packageState)
+ }
+ }
+ }
+
private fun MutateStateScope.shouldGrantPermissionByProtectionFlags(
packageState: PackageState,
permission: Permission
@@ -814,26 +986,33 @@ class UidPermissionPolicy : SchemePolicy() {
return uid == ownerUid
}
- override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
- // TODO: STOPSHIP: Remove this check or at least turn into logging.
- check(packageName !in newState.systemState.disabledSystemPackageStates) {
- "Package $packageName reported as removed before disabled system package is enabled"
- }
+ override fun BinaryXmlPullParser.parseSystemState(state: AccessState) {
+ with(persistence) { this@parseSystemState.parseSystemState(state) }
+ }
+
+ override fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {
+ with(persistence) { this@serializeSystemState.serializeSystemState(state) }
}
- override fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) {
- with(persistence) { this@parseSystemState.parseSystemState(systemState) }
+ override fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
+ with(persistence) { this@parseUserState.parseUserState(state, userId) }
}
- override fun BinaryXmlSerializer.serializeSystemState(systemState: SystemState) {
- with(persistence) { this@serializeSystemState.serializeSystemState(systemState) }
+ override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+ with(persistence) { this@serializeUserState.serializeUserState(state, userId) }
}
- fun GetStateScope.getPermissionGroup(permissionGroupName: String): PermissionGroupInfo? =
- state.systemState.permissionGroups[permissionGroupName]
+ /**
+ * returns all permission group definitions available in the system
+ */
+ fun GetStateScope.getPermissionGroups(): IndexedMap<String, PermissionGroupInfo> =
+ state.systemState.permissionGroups
- fun GetStateScope.getPermission(permissionName: String): Permission? =
- state.systemState.permissions[permissionName]
+ /**
+ * returns all permission definitions available in the system
+ */
+ fun GetStateScope.getPermissions(): IndexedMap<String, Permission> =
+ state.systemState.permissions
fun GetStateScope.getUidPermissionFlags(appId: Int, userId: Int): IndexedMap<String, Int>? =
state.userStates[userId]?.uidPermissionFlags?.get(appId)
@@ -844,12 +1023,11 @@ class UidPermissionPolicy : SchemePolicy() {
permissionName: String
): Int = getPermissionFlags(state, appId, userId, permissionName)
- private fun MutateStateScope.getPermissionFlags(
+ private fun MutateStateScope.getOldStatePermissionFlags(
appId: Int,
userId: Int,
- permissionName: String,
- state: AccessState = newState
- ): Int = getPermissionFlags(state, appId, userId, permissionName)
+ permissionName: String
+ ): Int = getPermissionFlags(oldState, appId, userId, permissionName)
private fun getPermissionFlags(
state: AccessState,
@@ -915,13 +1093,24 @@ class UidPermissionPolicy : SchemePolicy() {
private const val PLATFORM_PACKAGE_NAME = "android"
// A set of permissions that we don't want to revoke when they are no longer implicit.
- private val RETAIN_IMPLICIT_GRANT_PERMISSIONS = indexedSetOf(
+ private val RETAIN_IMPLICIT_FLAGS_PERMISSIONS = indexedSetOf(
Manifest.permission.ACCESS_MEDIA_LOCATION,
Manifest.permission.ACTIVITY_RECOGNITION,
Manifest.permission.READ_MEDIA_AUDIO,
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO,
)
+
+ // TODO: also add the permission NEARBY_WIFI_DEVICES to this set
+ private val NEARBY_DEVICES_PERMISSIONS = indexedSetOf(
+ Manifest.permission.BLUETOOTH_ADVERTISE,
+ Manifest.permission.BLUETOOTH_CONNECT,
+ Manifest.permission.BLUETOOTH_SCAN
+ )
+
+ private val NOTIFICATION_PERMISSIONS = indexedSetOf(
+ Manifest.permission.POST_NOTIFICATIONS
+ )
}
fun interface OnPermissionFlagsChangedListener {
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/affected_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/affected_cpus
index 06717bdd7f35..0cfbf08886fc 100644
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/affected_cpus
+++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/affected_cpus
@@ -1 +1 @@
-2,3
+2
diff --git a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java
index 81af775a458c..9f3bc3358a4c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java
@@ -16,6 +16,9 @@
package com.android.server.cpu;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.cpu.CpuInfoReader.CpuInfo.MISSING_FREQUENCY;
import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_BACKGROUND;
import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_TOP_APP;
@@ -23,9 +26,8 @@ import static com.google.common.truth.Truth.assertWithMessage;
import android.content.Context;
import android.content.res.AssetManager;
-import android.util.Slog;
-
-import androidx.test.platform.app.InstrumentationRegistry;
+import android.util.Log;
+import android.util.SparseArray;
import com.android.server.ExtendedMockitoTestCase;
@@ -34,20 +36,14 @@ import libcore.io.Streams;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.List;
import java.util.Objects;
-/**
- * <p>This class contains unit tests for the {@link CpuInfoReader}.
- */
-@RunWith(MockitoJUnitRunner.class)
+/** This class contains unit tests for the {@link CpuInfoReader}. */
public final class CpuInfoReaderTest extends ExtendedMockitoTestCase {
private static final String TAG = CpuInfoReaderTest.class.getSimpleName();
private static final String ROOT_DIR_NAME = "CpuInfoReaderTest";
@@ -68,334 +64,354 @@ public final class CpuInfoReaderTest extends ExtendedMockitoTestCase {
private static final String EMPTY_DIR = "empty_dir";
private static final String EMPTY_FILE = "empty_file";
- private final Context mContext =
- InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private final Context mContext = getInstrumentation().getTargetContext();
private final File mCacheRoot = new File(mContext.getCacheDir(), ROOT_DIR_NAME);
private final AssetManager mAssetManager = mContext.getAssets();
- private CpuInfoReader mCpuInfoReader;
-
@Before
public void setUp() throws Exception {
copyAssets(ROOT_DIR_NAME, mContext.getCacheDir());
- assertWithMessage("Cache root dir %s", mCacheRoot.getAbsolutePath())
+ assertWithMessage("Cache root dir %s exists", mCacheRoot.getAbsolutePath())
.that(mCacheRoot.exists()).isTrue();
}
@After
public void tearDown() throws Exception {
if (!deleteDirectory(mCacheRoot)) {
- Slog.e(TAG, "Failed to delete cache root directory " + mCacheRoot.getAbsolutePath());
+ Log.e(TAG, "Failed to delete cache root directory " + mCacheRoot.getAbsolutePath());
}
}
@Test
public void testReadCpuInfoWithTimeInState() throws Exception {
- mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
+ CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(VALID_PROC_STAT));
- mCpuInfoReader.init();
- List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos();
- List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of(
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 488_095, /* maxCpuFreqKHz= */ 2_500_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
- /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
- /* idleTimeMillis= */ 409_036_950,
- /* iowaitTimeMillis= */ 1_322_810, /* irqTimeMillis= */ 8_146_740,
- /* softirqTimeMillis= */ 428_970, /* stealTimeMillis= */ 81_950,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 502_380, /* maxCpuFreqKHz= */ 2_800_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
- /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
- /* idleTimeMillis= */ 402_707_120,
- /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940,
- /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 464_285, /* maxCpuFreqKHz= */ 2_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
- /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
- /* idleTimeMillis= */ 402_717_120,
- /* iowaitTimeMillis= */ 1_166_960, /* irqTimeMillis= */ 14_796_940,
- /* softirqTimeMillis= */ 1_478_130, /* stealTimeMillis= */ 88_780,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 464_285, /* maxCpuFreqKHz= */ 2_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
- /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
- /* idleTimeMillis= */ 409_136_950,
- /* iowaitTimeMillis= */ 1_332_810, /* irqTimeMillis= */ 8_136_740,
- /* softirqTimeMillis= */ 438_970, /* stealTimeMillis= */ 71_950,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)));
-
- assertWithMessage("Cpu infos").that(actualCpuInfos)
- .containsExactlyElementsIn(expectedCpuInfos);
-
- mCpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_2_DIR));
- mCpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
-
- actualCpuInfos = mCpuInfoReader.readCpuInfos();
- expectedCpuInfos = List.of(
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 419_354, /* maxCpuFreqKHz= */ 2_500_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
- /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
- /* idleTimeMillis= */ 110_000_000,
- /* iowaitTimeMillis= */ 1_100_000, /* irqTimeMillis= */ 1_400_000,
- /* softirqTimeMillis= */ 80_000, /* stealTimeMillis= */ 21_000,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 429_032, /* maxCpuFreqKHz= */ 2_800_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000,
- /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
- /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000,
- /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000,
- /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
- /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 403_225, /* maxCpuFreqKHz= */ 2_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
- /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0,
- /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000,
- /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000,
- /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
- /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 403_225, /* maxCpuFreqKHz= */ 2_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000,
- /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000,
- /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000,
- /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000,
- /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0,
- /* guestNiceTimeMillis= */ 0)));
-
- assertWithMessage("Second snapshot of cpu infos").that(actualCpuInfos)
- .containsExactlyElementsIn(expectedCpuInfos);
+
+ SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
+ SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
+ expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
+ /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
+ /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
+ /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
+ /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970,
+ /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
+ /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
+ /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
+ /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
+ /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
+ /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
+ /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
+ /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
+ /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
+ /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130,
+ /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
+ /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
+ /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
+ /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810,
+ /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970,
+ /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+
+ compareCpuInfos("CPU infos first snapshot", expectedCpuInfos, actualCpuInfos);
+
+ cpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_2_DIR));
+ cpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
+
+ actualCpuInfos = cpuInfoReader.readCpuInfos();
+
+ expectedCpuInfos.clear();
+ expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
+ /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 419_354,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
+ /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
+ /* idleTimeMillis= */ 110_000_000, /* iowaitTimeMillis= */ 1_100_000,
+ /* irqTimeMillis= */ 1_400_000, /* softirqTimeMillis= */ 80_000,
+ /* stealTimeMillis= */ 21_000, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_800_000,
+ /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 429_032,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000,
+ /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
+ /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000,
+ /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000,
+ /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000,
+ /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 403_225,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
+ /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0,
+ /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000,
+ /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000,
+ /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ false, /* curCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000,
+ /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000,
+ /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000,
+ /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000,
+ /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+
+ compareCpuInfos("CPU infos second snapshot", expectedCpuInfos, actualCpuInfos);
}
@Test
public void testReadCpuInfoWithoutTimeInState() throws Exception {
- mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
+ CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_DIR),
getCacheFile(VALID_PROC_STAT));
- mCpuInfoReader.init();
- List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos();
- List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of(
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 1_230_000, /* maxCpuFreqKHz= */ 2_500_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
- /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
- /* idleTimeMillis= */ 409_036_950,
- /* iowaitTimeMillis= */ 1_322_810, /* irqTimeMillis= */ 8_146_740,
- /* softirqTimeMillis= */ 428_970, /* stealTimeMillis= */ 81_950,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 1_450_000, /* maxCpuFreqKHz= */ 2_800_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
- /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
- /* idleTimeMillis= */ 402_707_120,
- /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940,
- /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 1_000_000, /* maxCpuFreqKHz= */ 2_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
- /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
- /* idleTimeMillis= */ 402_717_120,
- /* iowaitTimeMillis= */ 1_166_960, /* irqTimeMillis= */ 14_796_940,
- /* softirqTimeMillis= */ 1_478_130, /* stealTimeMillis= */ 88_780,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 1_000_000, /* maxCpuFreqKHz= */ 2_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
- /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
- /* idleTimeMillis= */ 409_136_950,
- /* iowaitTimeMillis= */ 1_332_810, /* irqTimeMillis= */ 8_136_740,
- /* softirqTimeMillis= */ 438_970, /* stealTimeMillis= */ 71_950,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)));
-
- assertWithMessage("Cpu infos").that(actualCpuInfos)
- .containsExactlyElementsIn(expectedCpuInfos);
-
- mCpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_2_DIR));
- mCpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
-
- actualCpuInfos = mCpuInfoReader.readCpuInfos();
- expectedCpuInfos = List.of(
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 1_000_000, /* maxCpuFreqKHz= */ 2_500_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
- /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
- /* idleTimeMillis= */ 110_000_000,
- /* iowaitTimeMillis= */ 1_100_000, /* irqTimeMillis= */ 1_400_000,
- /* softirqTimeMillis= */ 80_000, /* stealTimeMillis= */ 21_000,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 2_800_000, /* maxCpuFreqKHz= */ 2_800_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000,
- /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
- /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000,
- /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000,
- /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
- /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 2_000_000, /* maxCpuFreqKHz= */ 2_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
- /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0,
- /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000,
- /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000,
- /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
- /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 2_000_000, /* maxCpuFreqKHz= */ 2_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000,
- /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000,
- /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000,
- /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000,
- /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0,
- /* guestNiceTimeMillis= */ 0)));
-
- assertWithMessage("Second snapshot of cpu infos").that(actualCpuInfos)
- .containsExactlyElementsIn(expectedCpuInfos);
+
+ SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
+ SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
+ expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
+ /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
+ /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
+ /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
+ /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970,
+ /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
+ /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
+ /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
+ /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
+ /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
+ /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
+ /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
+ /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
+ /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
+ /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130,
+ /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
+ /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
+ /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
+ /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810,
+ /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970,
+ /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+
+ compareCpuInfos("CPU infos first snapshot without time_in_state file", expectedCpuInfos,
+ actualCpuInfos);
+
+ cpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_2_DIR));
+ cpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
+
+ actualCpuInfos = cpuInfoReader.readCpuInfos();
+
+ expectedCpuInfos.clear();
+ expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
+ /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
+ /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
+ /* idleTimeMillis= */ 110_000_000, /* iowaitTimeMillis= */ 1_100_000,
+ /* irqTimeMillis= */ 1_400_000, /* softirqTimeMillis= */ 80_000,
+ /* stealTimeMillis= */ 21_000, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_800_000,
+ /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000,
+ /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
+ /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000,
+ /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000,
+ /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000,
+ /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
+ /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0,
+ /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000,
+ /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000,
+ /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000,
+ /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000,
+ /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000,
+ /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000,
+ /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000,
+ /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+
+ compareCpuInfos("CPU infos second snapshot without time_in_state file", expectedCpuInfos,
+ actualCpuInfos);
}
@Test
public void testReadCpuInfoWithCorruptedCpuset() throws Exception {
- mCpuInfoReader = new CpuInfoReader(getCacheFile(CORRUPTED_CPUSET_DIR),
+ CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(CORRUPTED_CPUSET_DIR),
getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR),
getCacheFile(VALID_PROC_STAT));
- mCpuInfoReader.init();
- List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos();
- List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of(
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 488_095, /* maxCpuFreqKHz= */ 2_500_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
- /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
- /* idleTimeMillis= */ 409_036_950,
- /* iowaitTimeMillis= */ 1_322_810, /* irqTimeMillis= */ 8_146_740,
- /* softirqTimeMillis= */ 428_970, /* stealTimeMillis= */ 81_950,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 502_380, /* maxCpuFreqKHz= */ 2_800_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
- /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
- /* idleTimeMillis= */ 402_707_120,
- /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940,
- /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 464_285, /* maxCpuFreqKHz= */ 2_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
- /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
- /* idleTimeMillis= */ 402_717_120,
- /* iowaitTimeMillis= */ 1_166_960, /* irqTimeMillis= */ 14_796_940,
- /* softirqTimeMillis= */ 1_478_130, /* stealTimeMillis= */ 88_780,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)));
-
- assertWithMessage("Cpu infos").that(actualCpuInfos)
- .containsExactlyElementsIn(expectedCpuInfos);
+
+ SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
+ SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
+ expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
+ /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
+ /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
+ /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
+ /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970,
+ /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
+ /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
+ /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
+ /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
+ /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
+ /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
+ /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
+ /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
+ /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
+ /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130,
+ /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+
+ compareCpuInfos("CPU infos with corrupted cpuset", expectedCpuInfos, actualCpuInfos);
}
@Test
public void testReadCpuInfoWithCorruptedCpufreq() throws Exception {
- mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
+ CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
getCacheFile(CORRUPTED_CPUFREQ_DIR), getCacheFile(VALID_PROC_STAT));
- mCpuInfoReader.init();
- List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos();
- List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of(
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 3_000_000, /* maxCpuFreqKHz= */ 1_000_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
- /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
- /* idleTimeMillis= */ 402_707_120,
- /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940,
- /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
- /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
- /* idleTimeMillis= */ 402_717_120,
- /* iowaitTimeMillis= */ 1_166_960, /* irqTimeMillis= */ 14_796_940,
- /* softirqTimeMillis= */ 1_478_130, /* stealTimeMillis= */ 88_780,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
- /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
- /* idleTimeMillis= */ 409_136_950,
- /* iowaitTimeMillis= */ 1_332_810, /* irqTimeMillis= */ 8_136_740,
- /* softirqTimeMillis= */ 438_970, /* stealTimeMillis= */ 71_950,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)));
-
- assertWithMessage("Cpu infos").that(actualCpuInfos)
- .containsExactlyElementsIn(expectedCpuInfos);
+
+ SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
+ SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
+ expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 3_000_000,
+ /* maxCpuFreqKHz= */ 1_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
+ /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
+ /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
+ /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
+ /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ true, /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2,
+ /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
+ /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
+ /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
+ /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130,
+ /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
+ FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
+ /* isOnline= */ true, /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2,
+ /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
+ /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
+ /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810,
+ /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970,
+ /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+
+ compareCpuInfos("CPU infos with corrupted CPU frequency", expectedCpuInfos,
+ actualCpuInfos);
}
@Test
- public void testReadCpuInfoCorruptedProcStat() throws Exception {
- mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
+ public void testReadCpuInfoWithCorruptedProcStat() throws Exception {
+ CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR),
getCacheFile(CORRUPTED_PROC_STAT));
- mCpuInfoReader.init();
- List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos();
- List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of(
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 488_095, /* maxCpuFreqKHz= */ 2_500_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
- /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
- /* idleTimeMillis= */ 409_036_950,
- /* iowaitTimeMillis= */ 1_322_810, /* irqTimeMillis= */ 8_146_740,
- /* softirqTimeMillis= */ 428_970, /* stealTimeMillis= */ 81_950,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)),
- new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
- FLAG_CPUSET_CATEGORY_TOP_APP,
- /* curCpuFreqKHz= */ 502_380, /* maxCpuFreqKHz= */ 2_800_000,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
- /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
- /* idleTimeMillis= */ 402_707_120,
- /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940,
- /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780,
- /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)));
-
- assertWithMessage("Cpu infos").that(actualCpuInfos)
- .containsExactlyElementsIn(expectedCpuInfos);
+
+ SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
+ SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
+ expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
+ /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
+ /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
+ /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
+ /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970,
+ /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+ expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
+ FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
+ /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
+ new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
+ /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
+ /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
+ /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
+ /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
+ /* guestNiceTimeMillis= */ 0)));
+
+ compareCpuInfos("CPU infos with corrupted proc stat", expectedCpuInfos, actualCpuInfos);
}
@Test
public void testReadCpuInfoWithEmptyCpuset() throws Exception {
File emptyDir = getCacheFile(EMPTY_DIR);
assertWithMessage("Make empty dir %s", emptyDir).that(emptyDir.mkdir()).isTrue();
- mCpuInfoReader = new CpuInfoReader(emptyDir, getCacheFile(
+ CpuInfoReader cpuInfoReader = new CpuInfoReader(emptyDir, getCacheFile(
VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR),
getCacheFile(VALID_PROC_STAT));
- assertWithMessage("Init CPU reader info").that(mCpuInfoReader.init()).isFalse();
+ assertWithMessage("Init CPU reader info").that(cpuInfoReader.init()).isFalse();
- assertWithMessage("Cpu infos").that(mCpuInfoReader.readCpuInfos()).isEmpty();
+ assertWithMessage("Cpu infos with empty cpuset").that(cpuInfoReader.readCpuInfos())
+ .isNull();
}
@Test
public void testReadCpuInfoWithEmptyCpufreq() throws Exception {
File emptyDir = getCacheFile(EMPTY_DIR);
assertWithMessage("Make empty dir %s", emptyDir).that(emptyDir.mkdir()).isTrue();
- mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), emptyDir,
+ CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), emptyDir,
getCacheFile(VALID_PROC_STAT));
- assertWithMessage("Init CPU reader info").that(mCpuInfoReader.init()).isFalse();
+ assertWithMessage("Init CPU reader info").that(cpuInfoReader.init()).isFalse();
- assertWithMessage("Cpu infos").that(mCpuInfoReader.readCpuInfos()).isEmpty();
+ assertWithMessage("Cpu infos with empty CPU frequency").that(cpuInfoReader.readCpuInfos())
+ .isNull();
}
@Test
@@ -403,10 +419,25 @@ public final class CpuInfoReaderTest extends ExtendedMockitoTestCase {
File emptyFile = getCacheFile(EMPTY_FILE);
assertWithMessage("Create empty file %s", emptyFile).that(emptyFile.createNewFile())
.isTrue();
- mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
+ CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(EMPTY_FILE));
- assertWithMessage("Cpu infos").that(mCpuInfoReader.readCpuInfos()).isEmpty();
+ assertWithMessage("Cpu infos with empty proc stat").that(cpuInfoReader.readCpuInfos())
+ .isNull();
+ }
+
+ private static void compareCpuInfos(String message,
+ SparseArray<CpuInfoReader.CpuInfo> expected,
+ SparseArray<CpuInfoReader.CpuInfo> actual) {
+ assertWithMessage("%s. Total CPU infos", message).that(actual.size())
+ .isEqualTo(expected.size());
+ for (int i = 0; i < expected.size(); i++) {
+ int cpuCoreId = expected.keyAt(i);
+ CpuInfoReader.CpuInfo expectedCpuInfo = expected.valueAt(i);
+ CpuInfoReader.CpuInfo actualCpuInfo = actual.get(cpuCoreId);
+ assertWithMessage("%s. Core %d's CPU info", message, cpuCoreId).that(actualCpuInfo)
+ .isEqualTo(expectedCpuInfo);
+ }
}
private File getCacheFile(String assetName) {
@@ -424,11 +455,18 @@ public final class CpuInfoReaderTest extends ExtendedMockitoTestCase {
return;
}
assertWithMessage("Make target directory %s", target).that(target.mkdir()).isTrue();
- for (int i = 0; i < assets.length; i++) {
- copyAssets(String.format("%s%s%s", assetPath, File.separator, assets[i]), targetRoot);
+ for (String assetName : assets) {
+ copyAssets(String.format("%s%s%s", assetPath, File.separator, assetName), targetRoot);
}
}
+ private static CpuInfoReader newCpuInfoReader(File cpusetDir, File cpuFreqDir,
+ File procStatFile) {
+ CpuInfoReader cpuInfoReader = new CpuInfoReader(cpusetDir, cpuFreqDir, procStatFile);
+ assertWithMessage("Initialize CPU info reader").that(cpuInfoReader.init()).isTrue();
+ return cpuInfoReader;
+ }
+
private static boolean deleteDirectory(File rootDir) {
if (!rootDir.exists() || !rootDir.isDirectory()) {
return false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
index 480a4f358bc0..0dfad43599de 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -505,27 +505,27 @@ public final class JobConcurrencyManagerTest {
assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
doReturn(false).when(job).shouldTreatAsExpeditedJob();
- doReturn(false).when(job).shouldTreatAsUserInitiated();
+ doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
doReturn(true).when(job).shouldTreatAsExpeditedJob();
- doReturn(false).when(job).shouldTreatAsUserInitiated();
+ doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
doReturn(false).when(job).shouldTreatAsExpeditedJob();
- doReturn(true).when(job).shouldTreatAsUserInitiated();
+ doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
doReturn(false).when(job).shouldTreatAsExpeditedJob();
- doReturn(true).when(job).shouldTreatAsUserInitiated();
+ doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
doReturn(true).when(job).shouldTreatAsExpeditedJob();
- doReturn(false).when(job).shouldTreatAsUserInitiated();
+ doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 0eeebca61094..2c103298b557 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -249,8 +249,8 @@ public class JobSchedulerServiceTest {
when(ejHighDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false);
when(jobHigh.shouldTreatAsExpeditedJob()).thenReturn(false);
when(jobDef.shouldTreatAsExpeditedJob()).thenReturn(false);
- when(jobUI.shouldTreatAsUserInitiated()).thenReturn(true);
- when(jobUIDT.shouldTreatAsUserInitiated()).thenReturn(true);
+ when(jobUI.shouldTreatAsUserInitiatedJob()).thenReturn(true);
+ when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true);
ConnectivityController connectivityController = mService.getConnectivityController();
spyOn(connectivityController);
@@ -335,8 +335,8 @@ public class JobSchedulerServiceTest {
spyOn(jobUI);
spyOn(jobUIDT);
- when(jobUI.shouldTreatAsUserInitiated()).thenReturn(true);
- when(jobUIDT.shouldTreatAsUserInitiated()).thenReturn(true);
+ when(jobUI.shouldTreatAsUserInitiatedJob()).thenReturn(true);
+ when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true);
QuotaController quotaController = mService.getQuotaController();
spyOn(quotaController);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index d72ccf813445..9aef674ed986 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -2179,10 +2179,10 @@ public class QuotaControllerTest {
mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, jobCount);
assertFalse(mQuotaController
.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- doReturn(false).when(job).shouldTreatAsUserInitiated();
+ doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
assertFalse(mQuotaController.isWithinQuotaLocked(job));
// User-initiated job should still be allowed.
- doReturn(true).when(job).shouldTreatAsUserInitiated();
+ doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
assertTrue(mQuotaController.isWithinQuotaLocked(job));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
index 49426901b142..90672851cdff 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
@@ -198,10 +198,10 @@ public class ThermalStatusRestrictionTest {
when(ejRetried.shouldTreatAsExpeditedJob()).thenReturn(true);
when(ejRunning.shouldTreatAsExpeditedJob()).thenReturn(true);
when(ejRunningLong.shouldTreatAsExpeditedJob()).thenReturn(true);
- when(ui.shouldTreatAsUserInitiated()).thenReturn(true);
- when(uiRetried.shouldTreatAsUserInitiated()).thenReturn(true);
- when(uiRunning.shouldTreatAsUserInitiated()).thenReturn(true);
- when(uiRunningLong.shouldTreatAsUserInitiated()).thenReturn(true);
+ when(ui.shouldTreatAsUserInitiatedJob()).thenReturn(true);
+ when(uiRetried.shouldTreatAsUserInitiatedJob()).thenReturn(true);
+ when(uiRunning.shouldTreatAsUserInitiatedJob()).thenReturn(true);
+ when(uiRunningLong.shouldTreatAsUserInitiatedJob()).thenReturn(true);
when(ejRetried.getNumPreviousAttempts()).thenReturn(1);
when(uiRetried.getNumPreviousAttempts()).thenReturn(2);
when(mJobSchedulerService.isCurrentlyRunningLocked(jobLowPriorityRunning)).thenReturn(true);
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index 1a6dae372a18..f3ad6d87dbe9 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -34,6 +34,7 @@
showInLauncher='2020'
startWithParent='false'
useParentsContacts='false'
+ crossProfileIntentFilterAccessControl='20'
/>
</profile-type>
<profile-type name='custom.test.1' max-allowed-per-parent='14' />
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index e95924ad7109..4cdca268fc4b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -62,6 +62,7 @@ import android.security.KeyStore;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.biometrics.log.BiometricContext;
import org.junit.Before;
import org.junit.Test;
@@ -81,6 +82,7 @@ public class AuthSessionTest {
private static final long TEST_REQUEST_ID = 22;
@Mock private Context mContext;
+ @Mock private BiometricContext mBiometricContext;
@Mock private ITrustManager mTrustManager;
@Mock private DevicePolicyManager mDevicePolicyManager;
@Mock private BiometricService.SettingObserver mSettingObserver;
@@ -102,6 +104,8 @@ public class AuthSessionTest {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mClientReceiver.asBinder()).thenReturn(mock(Binder.class));
+ when(mBiometricContext.updateContext(any(), anyBoolean()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
mRandom = new Random();
mToken = new Binder();
mSensors = new ArrayList<>();
@@ -437,9 +441,9 @@ public class AuthSessionTest {
final PreAuthInfo preAuthInfo = createPreAuthInfo(sensors, userId, promptInfo,
checkDevicePolicyManager);
- return new AuthSession(mContext, mStatusBarService, mSysuiReceiver, mKeyStore,
- mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId, operationId, userId,
- mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo,
+ return new AuthSession(mContext, mBiometricContext, mStatusBarService, mSysuiReceiver,
+ mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId,
+ operationId, userId, mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo,
false /* debugEnabled */, mFingerprintSensorProps);
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 0d6d1a2e93f9..7d6110e82139 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -19,6 +19,7 @@ package com.android.server.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED;
@@ -65,12 +66,16 @@ import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.hardware.display.DisplayManagerGlobal;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.security.KeyStore;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -134,6 +139,8 @@ public class BiometricServiceTest {
@Mock
DevicePolicyManager mDevicePolicyManager;
@Mock
+ private WindowManager mWindowManager;
+ @Mock
private IStatusBarService mStatusBarService;
@Mock
private ISessionListener mSessionListener;
@@ -174,9 +181,13 @@ public class BiometricServiceTest {
when(mResources.getString(R.string.biometric_error_user_canceled))
.thenReturn(ERROR_USER_CANCELED);
+ when(mWindowManager.getDefaultDisplay()).thenReturn(
+ new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
+ new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS));
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- mBiometricContextProvider = new BiometricContextProvider(mAmbientDisplayConfiguration,
- mStatusBarService, null /* handler */, mAuthSessionCoordinator);
+ mBiometricContextProvider = new BiometricContextProvider(mContext, mWindowManager,
+ mAmbientDisplayConfiguration, mStatusBarService, null /* handler */,
+ mAuthSessionCoordinator);
when(mInjector.getBiometricContext(any())).thenReturn(mBiometricContextProvider);
final String[] config = {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
index 58f338c7a5bb..2ccdda81b755 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.log;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
@@ -27,14 +29,22 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.StatusBarManager;
+import android.content.Intent;
import android.hardware.biometrics.IBiometricContextListener;
+import android.hardware.biometrics.IBiometricContextListener.FoldState;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.logging.InstanceId;
import com.android.internal.statusbar.ISessionListener;
@@ -58,6 +68,9 @@ public class BiometricContextProviderTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
@Mock
private IStatusBarService mStatusBarService;
@@ -65,16 +78,22 @@ public class BiometricContextProviderTest {
private ISessionListener mSessionListener;
@Mock
private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+ @Mock
+ private WindowManager mWindowManager;
- private OperationContext mOpContext = new OperationContext();
+ private OperationContextExt mOpContext = new OperationContextExt();
private IBiometricContextListener mListener;
private BiometricContextProvider mProvider;
@Before
public void setup() throws RemoteException {
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- mProvider = new BiometricContextProvider(mAmbientDisplayConfiguration, mStatusBarService,
- null /* handler */, null /* authSessionCoordinator */);
+ when(mWindowManager.getDefaultDisplay()).thenReturn(
+ new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
+ new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS));
+ mProvider = new BiometricContextProvider(mContext, mWindowManager,
+ mAmbientDisplayConfiguration, mStatusBarService, null /* handler */,
+ null /* authSessionCoordinator */);
ArgumentCaptor<IBiometricContextListener> captor =
ArgumentCaptor.forClass(IBiometricContextListener.class);
verify(mStatusBarService).setBiometicContextListener(captor.capture());
@@ -112,11 +131,37 @@ public class BiometricContextProviderTest {
}
@Test
+ public void testGetDockedState() {
+ final List<Integer> states = List.of(Intent.EXTRA_DOCK_STATE_DESK,
+ Intent.EXTRA_DOCK_STATE_CAR, Intent.EXTRA_DOCK_STATE_UNDOCKED);
+
+ for (int state : states) {
+ final Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_DOCK_STATE, state);
+ mProvider.mDockStateReceiver.onReceive(mContext, intent);
+
+ assertThat(mProvider.getDockedState()).isEqualTo(state);
+ }
+ }
+
+ @Test
+ public void testGetFoldState() throws RemoteException {
+ final List<Integer> states = List.of(FoldState.FULLY_CLOSED, FoldState.FULLY_OPENED,
+ FoldState.UNKNOWN, FoldState.HALF_OPENED);
+
+ for (int state : states) {
+ mListener.onFoldChanged(state);
+
+ assertThat(mProvider.getFoldState()).isEqualTo(state);
+ }
+ }
+
+ @Test
public void testSubscribesToAod() throws RemoteException {
final List<Boolean> actual = new ArrayList<>();
mProvider.subscribe(mOpContext, ctx -> {
- assertThat(ctx).isSameInstanceAs(mOpContext);
+ assertThat(ctx).isSameInstanceAs(mOpContext.toAidlContext());
assertThat(mProvider.isAod()).isEqualTo(ctx.isAod);
assertThat(mProvider.isAwake()).isFalse();
actual.add(ctx.isAod);
@@ -134,7 +179,7 @@ public class BiometricContextProviderTest {
final List<Boolean> actual = new ArrayList<>();
mProvider.subscribe(mOpContext, ctx -> {
- assertThat(ctx).isSameInstanceAs(mOpContext);
+ assertThat(ctx).isSameInstanceAs(mOpContext.toAidlContext());
assertThat(ctx.isAod).isFalse();
assertThat(mProvider.isAod()).isFalse();
actual.add(mProvider.isAwake());
@@ -162,7 +207,7 @@ public class BiometricContextProviderTest {
mListener.onDozeChanged(true /* isDozing */, false /* isAwake */);
verify(emptyConsumer, never()).accept(any());
- verify(nonEmptyConsumer).accept(same(mOpContext));
+ verify(nonEmptyConsumer).accept(same(mOpContext.toAidlContext()));
}
@Test
@@ -170,45 +215,47 @@ public class BiometricContextProviderTest {
final int keyguardSessionId = 10;
final int bpSessionId = 20;
- assertThat(mProvider.getBiometricPromptSessionId()).isNull();
- assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
+ assertThat(mProvider.getBiometricPromptSessionInfo()).isNull();
+ assertThat(mProvider.getKeyguardEntrySessionInfo()).isNull();
mSessionListener.onSessionStarted(StatusBarManager.SESSION_KEYGUARD,
InstanceId.fakeInstanceId(keyguardSessionId));
- assertThat(mProvider.getBiometricPromptSessionId()).isNull();
- assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId);
+ assertThat(mProvider.getBiometricPromptSessionInfo()).isNull();
+ assertThat(mProvider.getKeyguardEntrySessionInfo().getId()).isEqualTo(keyguardSessionId);
mSessionListener.onSessionStarted(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
InstanceId.fakeInstanceId(bpSessionId));
- assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId);
- assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId);
+ assertThat(mProvider.getBiometricPromptSessionInfo().getId()).isEqualTo(bpSessionId);
+ assertThat(mProvider.getKeyguardEntrySessionInfo().getId()).isEqualTo(keyguardSessionId);
mSessionListener.onSessionEnded(StatusBarManager.SESSION_KEYGUARD,
InstanceId.fakeInstanceId(keyguardSessionId));
- assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId);
- assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
+ assertThat(mProvider.getBiometricPromptSessionInfo().getId()).isEqualTo(bpSessionId);
+ assertThat(mProvider.getKeyguardEntrySessionInfo()).isNull();
mSessionListener.onSessionEnded(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
InstanceId.fakeInstanceId(bpSessionId));
- assertThat(mProvider.getBiometricPromptSessionId()).isNull();
- assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
+ assertThat(mProvider.getBiometricPromptSessionInfo()).isNull();
+ assertThat(mProvider.getKeyguardEntrySessionInfo()).isNull();
}
@Test
public void testUpdate() throws RemoteException {
mListener.onDozeChanged(false /* isDozing */, false /* isAwake */);
- OperationContext context = mProvider.updateContext(mOpContext, false /* crypto */);
+
+ OperationContextExt context = mProvider.updateContext(mOpContext, false /* crypto */);
+ OperationContext aidlContext = context.toAidlContext();
// default state when nothing has been set
assertThat(context).isSameInstanceAs(mOpContext);
- assertThat(mOpContext.id).isEqualTo(0);
- assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN);
- assertThat(mOpContext.isAod).isEqualTo(false);
- assertThat(mOpContext.isCrypto).isEqualTo(false);
+ assertThat(aidlContext.id).isEqualTo(0);
+ assertThat(aidlContext.reason).isEqualTo(OperationReason.UNKNOWN);
+ assertThat(aidlContext.isAod).isEqualTo(false);
+ assertThat(aidlContext.isCrypto).isEqualTo(false);
for (int type : List.of(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
StatusBarManager.SESSION_KEYGUARD)) {
@@ -218,21 +265,23 @@ public class BiometricContextProviderTest {
mListener.onDozeChanged(aod /* isDozing */, false /* isAwake */);
mSessionListener.onSessionStarted(type, InstanceId.fakeInstanceId(id));
context = mProvider.updateContext(mOpContext, false /* crypto */);
+ aidlContext = context.toAidlContext();
assertThat(context).isSameInstanceAs(mOpContext);
- assertThat(mOpContext.id).isEqualTo(id);
- assertThat(mOpContext.reason).isEqualTo(reason(type));
- assertThat(mOpContext.isAod).isEqualTo(aod);
- assertThat(mOpContext.isCrypto).isEqualTo(false);
+ assertThat(aidlContext.id).isEqualTo(id);
+ assertThat(aidlContext.reason).isEqualTo(reason(type));
+ assertThat(aidlContext.isAod).isEqualTo(aod);
+ assertThat(aidlContext.isCrypto).isEqualTo(false);
mSessionListener.onSessionEnded(type, InstanceId.fakeInstanceId(id));
}
context = mProvider.updateContext(mOpContext, false /* crypto */);
+ aidlContext = context.toAidlContext();
assertThat(context).isSameInstanceAs(mOpContext);
- assertThat(mOpContext.id).isEqualTo(0);
- assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN);
- assertThat(mOpContext.isAod).isEqualTo(false);
- assertThat(mOpContext.isCrypto).isEqualTo(false);
+ assertThat(aidlContext.id).isEqualTo(0);
+ assertThat(aidlContext.reason).isEqualTo(OperationReason.UNKNOWN);
+ assertThat(aidlContext.isAod).isEqualTo(false);
+ assertThat(aidlContext.isCrypto).isEqualTo(false);
}
private static byte reason(int type) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index 88a9646cac8a..81dcf4838b07 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -32,7 +32,6 @@ import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.input.InputSensorInfo;
import android.platform.test.annotations.Presubmit;
import android.testing.TestableContext;
@@ -70,12 +69,12 @@ public class BiometricLoggerTest {
@Mock
private BaseClientMonitor mClient;
- private OperationContext mOpContext;
+ private OperationContextExt mOpContext;
private BiometricLogger mLogger;
@Before
public void setUp() {
- mOpContext = new OperationContext();
+ mOpContext = new OperationContextExt();
mContext.addMockSystemService(SensorManager.class, mSensorManager);
when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(
new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
new file mode 100644
index 000000000000..c7962c82471a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.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.server.biometrics.log;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.hardware.biometrics.IBiometricContextListener;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.OperationReason;
+import android.platform.test.annotations.Presubmit;
+import android.view.Surface;
+
+import static org.mockito.Mockito.when;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.InstanceId;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class OperationContextExtTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ @Test
+ public void hasAidlContext() {
+ OperationContextExt context = new OperationContextExt();
+ assertThat(context.toAidlContext()).isNotNull();
+
+ final OperationContext aidlContext = newAidlContext();
+
+ context = new OperationContextExt(aidlContext);
+ assertThat(context.toAidlContext()).isSameInstanceAs(aidlContext);
+
+ final int id = 5;
+ final byte reason = OperationReason.UNKNOWN;
+ aidlContext.id = id;
+ aidlContext.isAod = true;
+ aidlContext.isCrypto = true;
+ aidlContext.reason = reason;
+
+ assertThat(context.getId()).isEqualTo(id);
+ assertThat(context.isAod()).isTrue();
+ assertThat(context.isCrypto()).isTrue();
+ assertThat(context.getReason()).isEqualTo(reason);
+ }
+
+ @Test
+ public void hasNoOrderWithoutSession() {
+ OperationContextExt context = new OperationContextExt();
+ assertThat(context.getOrderAndIncrement()).isEqualTo(-1);
+ assertThat(context.getOrderAndIncrement()).isEqualTo(-1);
+ }
+
+ @Test
+ public void updatesFromSourceForKeyguard() {
+ final BiometricContextSessionInfo info =
+ new BiometricContextSessionInfo(InstanceId.fakeInstanceId(9));
+ when(mBiometricContext.getKeyguardEntrySessionInfo()).thenReturn(info);
+ updatesFromSource(info, OperationReason.KEYGUARD);
+ }
+
+ @Test
+ public void updatesFromSourceForBiometricPrompt() {
+ final BiometricContextSessionInfo info =
+ new BiometricContextSessionInfo(InstanceId.fakeInstanceId(9));
+ when(mBiometricContext.getBiometricPromptSessionInfo()).thenReturn(info);
+ updatesFromSource(info, OperationReason.BIOMETRIC_PROMPT);
+ }
+
+ @Test
+ public void updatesFromSourceWithoutSession() {
+ updatesFromSource(null, OperationReason.UNKNOWN);
+ }
+
+ private void updatesFromSource(BiometricContextSessionInfo sessionInfo, int sessionType) {
+ final int rotation = Surface.ROTATION_270;
+ final int foldState = IBiometricContextListener.FoldState.HALF_OPENED;
+ final int dockState = Intent.EXTRA_DOCK_STATE_CAR;
+
+ when(mBiometricContext.getCurrentRotation()).thenReturn(rotation);
+ when(mBiometricContext.getFoldState()).thenReturn(foldState);
+ when(mBiometricContext.getDockedState()).thenReturn(dockState);
+ when(mBiometricContext.isDisplayOn()).thenReturn(true);
+
+ final OperationContextExt context = new OperationContextExt(newAidlContext());
+
+ assertThat(context.update(mBiometricContext)).isSameInstanceAs(context);
+
+ if (sessionInfo != null) {
+ assertThat(context.getId()).isEqualTo(sessionInfo.getId());
+ final int order = context.getOrderAndIncrement();
+ assertThat(context.getOrderAndIncrement()).isEqualTo(order + 1);
+ } else {
+ assertThat(context.getId()).isEqualTo(0);
+ }
+ assertThat(context.getReason()).isEqualTo(sessionType);
+ assertThat(context.getDockState()).isEqualTo(dockState);
+ assertThat(context.getFoldState()).isEqualTo(foldState);
+ assertThat(context.getOrientation()).isEqualTo(rotation);
+ assertThat(context.isDisplayOn()).isTrue();
+ }
+
+ private static OperationContext newAidlContext() {
+ final OperationContext aidlContext = new OperationContext();
+ aidlContext.id = -1;
+ aidlContext.isAod = false;
+ aidlContext.isCrypto = false;
+ aidlContext.reason = 0;
+ return aidlContext;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index d10d7d433e7a..139910ba8926 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -31,7 +31,6 @@ import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
import android.os.IBinder;
@@ -44,6 +43,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -96,7 +96,7 @@ public class FaceAuthenticationClientTest {
@Mock
private AuthSessionCoordinator mAuthSessionCoordinator;
@Captor
- private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -126,7 +126,7 @@ public class FaceAuthenticationClientTest {
order.verify(mBiometricContext).updateContext(
mOperationContextCaptor.capture(), anyBoolean());
order.verify(mHal).authenticateWithContext(
- eq(OP_ID), same(mOperationContextCaptor.getValue()));
+ eq(OP_ID), same(mOperationContextCaptor.getValue().toAidlContext()));
verify(mHal, never()).authenticate(anyLong());
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index 25135c6670db..e0fdb8cfc74e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.ISession;
import android.os.IBinder;
import android.os.RemoteException;
@@ -36,6 +35,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -74,7 +74,7 @@ public class FaceDetectClientTest {
@Mock
private Sensor.HalSessionCallback mHalSessionCallback;
@Captor
- private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -102,7 +102,8 @@ public class FaceDetectClientTest {
InOrder order = inOrder(mHal, mBiometricContext);
order.verify(mBiometricContext).updateContext(
mOperationContextCaptor.capture(), anyBoolean());
- order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue()));
+ order.verify(mHal).detectInteractionWithContext(
+ same(mOperationContextCaptor.getValue().toAidlContext()));
verify(mHal, never()).detectInteraction();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index 38e048bc1ba7..d75aca1ce480 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -25,7 +25,6 @@ import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
import android.os.IBinder;
@@ -38,6 +37,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -80,7 +80,7 @@ public class FaceEnrollClientTest {
@Mock
private Sensor.HalSessionCallback mHalSessionCallback;
@Captor
- private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -109,7 +109,7 @@ public class FaceEnrollClientTest {
order.verify(mBiometricContext).updateContext(
mOperationContextCaptor.capture(), anyBoolean());
order.verify(mHal).enrollWithContext(any(), anyByte(), any(), any(),
- same(mOperationContextCaptor.getValue()));
+ same(mOperationContextCaptor.getValue().toAidlContext()));
verify(mHal, never()).enroll(any(), anyByte(), any(), any());
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 22c53d349c5f..20beed0f6e82 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyFloat;
@@ -57,6 +59,7 @@ import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -131,7 +134,7 @@ public class FingerprintAuthenticationClientTest {
@Mock
private Clock mClock;
@Captor
- private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
private ArgumentCaptor<Consumer<OperationContext>> mContextInjector;
private final TestLooper mLooper = new TestLooper();
@@ -164,7 +167,7 @@ public class FingerprintAuthenticationClientTest {
order.verify(mBiometricContext).updateContext(
mOperationContextCaptor.capture(), anyBoolean());
order.verify(mHal).authenticateWithContext(
- eq(OP_ID), same(mOperationContextCaptor.getValue()));
+ eq(OP_ID), same(mOperationContextCaptor.getValue().toAidlContext()));
verify(mHal, never()).authenticate(anyLong());
}
@@ -236,9 +239,14 @@ public class FingerprintAuthenticationClientTest {
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
- verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
- OperationContext opContext = mOperationContextCaptor.getValue();
- verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
+ final ArgumentCaptor<OperationContext> captor =
+ ArgumentCaptor.forClass(OperationContext.class);
+ verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture());
+ OperationContext opContext = captor.getValue();
+ verify(mBiometricContext).subscribe(
+ mOperationContextCaptor.capture(), mContextInjector.capture());
+ assertThat(mOperationContextCaptor.getValue().toAidlContext())
+ .isSameInstanceAs(opContext);
mContextInjector.getValue().accept(opContext);
verify(mLuxProbe, never()).enable();
@@ -282,9 +290,14 @@ public class FingerprintAuthenticationClientTest {
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
- verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
- OperationContext opContext = mOperationContextCaptor.getValue();
- verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
+ final ArgumentCaptor<OperationContext> captor =
+ ArgumentCaptor.forClass(OperationContext.class);
+ verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture());
+ OperationContext opContext = captor.getValue();
+ verify(mBiometricContext).subscribe(
+ mOperationContextCaptor.capture(), mContextInjector.capture());
+ assertThat(opContext).isSameInstanceAs(
+ mOperationContextCaptor.getValue().toAidlContext());
mContextInjector.getValue().accept(opContext);
verify(mLuxProbe, never()).enable();
@@ -310,16 +323,21 @@ public class FingerprintAuthenticationClientTest {
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
- verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
- OperationContext opContext = mOperationContextCaptor.getValue();
+ final ArgumentCaptor<OperationContext> captor =
+ ArgumentCaptor.forClass(OperationContext.class);
+ verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture());
+ OperationContext opContext = captor.getValue();
// fake an update to the context
- verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
+ verify(mBiometricContext).subscribe(
+ mOperationContextCaptor.capture(), mContextInjector.capture());
+ assertThat(opContext).isSameInstanceAs(
+ mOperationContextCaptor.getValue().toAidlContext());
mContextInjector.getValue().accept(opContext);
verify(mHal).onContextChanged(eq(opContext));
client.stopHalOperation();
- verify(mBiometricContext).unsubscribe(same(opContext));
+ verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
index 4579fc15f661..2dbd8f69d694 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
@@ -37,6 +36,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -77,7 +77,7 @@ public class FingerprintDetectClientTest {
@Mock
private Sensor.HalSessionCallback mHalSessionCallback;
@Captor
- private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -107,7 +107,8 @@ public class FingerprintDetectClientTest {
InOrder order = inOrder(mHal, mBiometricContext);
order.verify(mBiometricContext).updateContext(
mOperationContextCaptor.capture(), anyBoolean());
- order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue()));
+ order.verify(mHal).detectInteractionWithContext(
+ same(mOperationContextCaptor.getValue().toAidlContext()));
verify(mHal, never()).detectInteraction();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index c3d478311423..26524d7df7c3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -48,6 +48,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -107,7 +108,7 @@ public class FingerprintEnrollClientTest {
@Mock
private Probe mLuxProbe;
@Captor
- private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
private ArgumentCaptor<PointerContext> mPointerContextCaptor;
@Captor
@@ -143,7 +144,8 @@ public class FingerprintEnrollClientTest {
InOrder order = inOrder(mHal, mBiometricContext);
order.verify(mBiometricContext).updateContext(
mOperationContextCaptor.capture(), anyBoolean());
- order.verify(mHal).enrollWithContext(any(), same(mOperationContextCaptor.getValue()));
+ order.verify(mHal).enrollWithContext(any(),
+ same(mOperationContextCaptor.getValue().toAidlContext()));
verify(mHal, never()).enroll(any());
}
@@ -234,16 +236,20 @@ public class FingerprintEnrollClientTest {
final FingerprintEnrollClient client = createClient();
client.start(mCallback);
- verify(mHal).enrollWithContext(any(), mOperationContextCaptor.capture());
- OperationContext opContext = mOperationContextCaptor.getValue();
+ final ArgumentCaptor<OperationContext> captor =
+ ArgumentCaptor.forClass(OperationContext.class);
+ verify(mHal).enrollWithContext(any(), captor.capture());
+ OperationContext opContext = captor.getValue();
// fake an update to the context
- verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
- mContextInjector.getValue().accept(opContext);
+ verify(mBiometricContext).subscribe(
+ mOperationContextCaptor.capture(), mContextInjector.capture());
+ mContextInjector.getValue().accept(
+ mOperationContextCaptor.getValue().toAidlContext());
verify(mHal).onContextChanged(eq(opContext));
client.stopHalOperation();
- verify(mBiometricContext).unsubscribe(same(opContext));
+ verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
}
@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 9c7c574f8e31..c1ee88c20096 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
@@ -54,6 +54,8 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class InputControllerTest {
+ private static final String LANGUAGE_TAG = "en-US";
+ private static final String LAYOUT_TYPE = "qwerty";
@Mock
private InputManagerInternal mInputManagerInternalMock;
@@ -100,7 +102,7 @@ public class InputControllerTest {
final IBinder device2Token = new Binder("device2");
mInputController.createKeyboard("keyboard", /*vendorId= */2, /*productId= */ 2,
- device2Token, 2);
+ device2Token, 2, LANGUAGE_TAG, LAYOUT_TYPE);
int device2Id = mInputController.getInputDeviceId(device2Token);
assertWithMessage("Different devices should have different id").that(
@@ -178,4 +180,17 @@ public class InputControllerTest {
verify(mInputManagerInternalMock).unsetTypeAssociation(
startsWith("virtualNavigationTouchpad:"));
}
+
+ @Test
+ public void createKeyboard_addAndRemoveKeyboardLayoutAssociation() {
+ final IBinder deviceToken = new Binder("device");
+
+ mInputController.createKeyboard("keyboard", /*vendorId= */2, /*productId= */ 2, deviceToken,
+ 2, LANGUAGE_TAG, LAYOUT_TYPE);
+ verify(mInputManagerInternalMock).addKeyboardLayoutAssociation(anyString(),
+ eq(LANGUAGE_TAG), eq(LAYOUT_TYPE));
+
+ mInputController.unregisterInputDevice(deviceToken);
+ verify(mInputManagerInternalMock).removeKeyboardLayoutAssociation(anyString());
+ }
}
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 31e53d56f520..0a4ae6f0b206 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
@@ -21,6 +21,7 @@ import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_INVALID;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
+import static android.content.Intent.ACTION_VIEW;
import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
import static com.google.common.truth.Truth.assertThat;
@@ -37,6 +38,7 @@ import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -49,6 +51,7 @@ import android.app.WindowConfiguration;
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDeviceActivityListener;
+import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
@@ -57,6 +60,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.hardware.Sensor;
@@ -73,6 +77,7 @@ import android.hardware.input.VirtualNavigationTouchpadConfig;
import android.hardware.input.VirtualTouchEvent;
import android.hardware.input.VirtualTouchscreenConfig;
import android.net.MacAddress;
+import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -159,6 +164,8 @@ public class VirtualDeviceManagerServiceTest {
.setProductId(PRODUCT_ID)
.setInputDeviceName(DEVICE_NAME)
.setAssociatedDisplayId(DISPLAY_ID)
+ .setLanguageTag(VirtualKeyboardConfig.DEFAULT_LANGUAGE_TAG)
+ .setLayoutType(VirtualKeyboardConfig.DEFAULT_LAYOUT_TYPE)
.build();
private static final VirtualMouseConfig MOUSE_CONFIG =
new VirtualMouseConfig.Builder()
@@ -184,6 +191,7 @@ public class VirtualDeviceManagerServiceTest {
.setInputDeviceName(DEVICE_NAME)
.setAssociatedDisplayId(DISPLAY_ID)
.build();
+ private static final String TEST_SITE = "http://test";
private Context mContext;
private InputManagerMockHelper mInputManagerMockHelper;
@@ -1340,6 +1348,99 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
+ public void canActivityBeLaunched_activityCanLaunch() {
+ Intent intent = new Intent(ACTION_VIEW, Uri.parse(TEST_SITE));
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ DISPLAY_ID);
+ ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoveDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isTrue();
+ }
+
+ @Test
+ public void canActivityBeLaunched_intentInterceptedWhenRegistered_activityNoLaunch()
+ throws RemoteException {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(TEST_SITE));
+
+ IVirtualDeviceIntentInterceptor.Stub interceptor =
+ mock(IVirtualDeviceIntentInterceptor.Stub.class);
+ doNothing().when(interceptor).onIntentIntercepted(any());
+ doReturn(interceptor).when(interceptor).asBinder();
+ doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
+
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ DISPLAY_ID);
+ ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoveDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
+ intentFilter.addDataScheme(IntentFilter.SCHEME_HTTP);
+ intentFilter.addDataScheme(IntentFilter.SCHEME_HTTPS);
+
+ // register interceptor and intercept intent
+ mDeviceImpl.registerIntentInterceptor(interceptor, intentFilter);
+ assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isFalse();
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(interceptor).onIntentIntercepted(intentCaptor.capture());
+ Intent cIntent = intentCaptor.getValue();
+ assertThat(cIntent).isNotNull();
+ assertThat(cIntent.getAction()).isEqualTo(Intent.ACTION_VIEW);
+ assertThat(cIntent.getData().toString()).isEqualTo(TEST_SITE);
+
+ // unregister interceptor and launch activity
+ mDeviceImpl.unregisterIntentInterceptor(interceptor);
+ assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isTrue();
+ }
+
+ @Test
+ public void canActivityBeLaunched_noMatchIntentFilter_activityLaunches()
+ throws RemoteException {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("testing"));
+
+ IVirtualDeviceIntentInterceptor.Stub interceptor =
+ mock(IVirtualDeviceIntentInterceptor.Stub.class);
+ doNothing().when(interceptor).onIntentIntercepted(any());
+ doReturn(interceptor).when(interceptor).asBinder();
+ doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
+
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ DISPLAY_ID);
+ ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoveDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
+ intentFilter.addDataScheme("mailto");
+
+ // register interceptor with different filter
+ mDeviceImpl.registerIntentInterceptor(interceptor, intentFilter);
+
+ assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isTrue();
+ }
+
+ @Test
public void nonRestrictedActivityOnRestrictedVirtualDisplay_startBlockedAlertActivity() {
Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"),
/* targetDisplayCategory= */ null);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index aaa13512af25..c27043565fcc 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -85,6 +85,7 @@ public class VirtualAudioControllerTest {
/* pipBlockedCallback= */ null,
/* activityBlockedCallback= */ null,
/* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ null,
/* displayCategories= */ new ArrayList<>(),
/* recentsPolicy= */
VirtualDeviceParams.RECENTS_POLICY_ALLOW_IN_HOST_DEVICE_RECENTS);
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 c1eafd9e35a6..82b89bbb0464 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -156,6 +156,14 @@ public final class DisplayDeviceConfigTest {
assertArrayEquals(new int[]{70, 80},
mDisplayDeviceConfig.getHighAmbientBrightnessThresholds());
+ assertEquals("sensor_12345",
+ mDisplayDeviceConfig.getScreenOffBrightnessSensor().type);
+ assertEquals("Sensor 12345",
+ mDisplayDeviceConfig.getScreenOffBrightnessSensor().name);
+
+ assertArrayEquals(new int[]{-1, 10, 20, 30, 40},
+ mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux());
+
// Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
}
@@ -227,6 +235,7 @@ public final class DisplayDeviceConfigTest {
HIGH_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE);
assertArrayEquals(mDisplayDeviceConfig.getHighAmbientBrightnessThresholds(),
HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE);
+
// Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
}
@@ -278,6 +287,10 @@ public final class DisplayDeviceConfigTest {
+ "<thermalStatusLimit>light</thermalStatusLimit>\n"
+ "<allowInLowPowerMode>false</allowInLowPowerMode>\n"
+ "</highBrightnessMode>\n"
+ + "<screenOffBrightnessSensor>\n"
+ + "<type>sensor_12345</type>\n"
+ + "<name>Sensor 12345</name>\n"
+ + "</screenOffBrightnessSensor>\n"
+ "<ambientBrightnessChangeThresholds>\n"
+ "<brighteningThresholds>\n"
+ "<minimum>10</minimum>\n"
@@ -458,6 +471,13 @@ public final class DisplayDeviceConfigTest {
+ "</blockingZoneThreshold>\n"
+ "</higherBlockingZoneConfigs>\n"
+ "</refreshRate>\n"
+ + "<screenOffBrightnessSensorValueToLux>\n"
+ + "<item>-1</item>\n"
+ + "<item>10</item>\n"
+ + "<item>20</item>\n"
+ + "<item>30</item>\n"
+ + "<item>40</item>\n"
+ + "</screenOffBrightnessSensorValueToLux>\n"
+ "</displayConfiguration>\n";
}
@@ -535,6 +555,7 @@ public final class DisplayDeviceConfigTest {
when(mResources.getIntArray(
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
.thenReturn(HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE);
+
mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java b/services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java
new file mode 100644
index 000000000000..ea04a193e569
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ScreenOffBrightnessSensorControllerTest {
+
+ private static final int[] SENSOR_TO_LUX = new int[]{-1, 10, 20, 30, 40};
+
+ private ScreenOffBrightnessSensorController mController;
+ private OffsettableClock mClock;
+ private Sensor mLightSensor;
+
+ @Mock SensorManager mSensorManager;
+ @Mock Handler mNoOpHandler;
+ @Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mClock = new OffsettableClock.Stopped();
+ mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+ mController = new ScreenOffBrightnessSensorController(
+ mSensorManager,
+ mLightSensor,
+ mNoOpHandler,
+ mClock::now,
+ SENSOR_TO_LUX,
+ mBrightnessMappingStrategy
+ );
+ }
+
+ @Test
+ public void testBrightness() throws Exception {
+ when(mSensorManager.registerListener(any(SensorEventListener.class), eq(mLightSensor),
+ eq(SensorManager.SENSOR_DELAY_NORMAL), any(Handler.class)))
+ .thenReturn(true);
+ mController.setLightSensorEnabled(true);
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(SensorManager.SENSOR_DELAY_NORMAL), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
+ assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT,
+ mController.getAutomaticScreenBrightness(), 0);
+
+ int sensorValue = 1;
+ float brightness = 0.2f;
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue));
+ when(mBrightnessMappingStrategy.getBrightness(SENSOR_TO_LUX[sensorValue]))
+ .thenReturn(brightness);
+ assertEquals(brightness, mController.getAutomaticScreenBrightness(), 0);
+
+ sensorValue = 2;
+ brightness = 0.4f;
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue));
+ when(mBrightnessMappingStrategy.getBrightness(SENSOR_TO_LUX[sensorValue]))
+ .thenReturn(brightness);
+ assertEquals(brightness, mController.getAutomaticScreenBrightness(), 0);
+
+ sensorValue = 3;
+ brightness = 0.6f;
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue));
+ when(mBrightnessMappingStrategy.getBrightness(SENSOR_TO_LUX[sensorValue]))
+ .thenReturn(brightness);
+ assertEquals(brightness, mController.getAutomaticScreenBrightness(), 0);
+
+ sensorValue = 4;
+ brightness = 0.8f;
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue));
+ when(mBrightnessMappingStrategy.getBrightness(SENSOR_TO_LUX[sensorValue]))
+ .thenReturn(brightness);
+ assertEquals(brightness, mController.getAutomaticScreenBrightness(), 0);
+
+ sensorValue = 5;
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue));
+ assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT,
+ mController.getAutomaticScreenBrightness(), 0);
+ }
+
+ @Test
+ public void testSensorValueValidTime() throws Exception {
+ when(mSensorManager.registerListener(any(SensorEventListener.class), eq(mLightSensor),
+ eq(SensorManager.SENSOR_DELAY_NORMAL), any(Handler.class)))
+ .thenReturn(true);
+ mController.setLightSensorEnabled(true);
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(SensorManager.SENSOR_DELAY_NORMAL), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1));
+ mController.setLightSensorEnabled(false);
+ assertNotEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT,
+ mController.getAutomaticScreenBrightness(), 0);
+
+ mClock.fastForward(2000);
+ mController.setLightSensorEnabled(false);
+ assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT,
+ mController.getAutomaticScreenBrightness(), 0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
index a419b3f80aac..90d9baed53d7 100644
--- a/services/tests/servicestests/src/com/android/server/display/TestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
@@ -28,13 +28,13 @@ import java.lang.reflect.Method;
public final class TestUtils {
- public static SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception {
+ public static SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception {
final Constructor<SensorEvent> constructor =
SensorEvent.class.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
final SensorEvent event = constructor.newInstance(1);
event.sensor = sensor;
- event.values[0] = lux;
+ event.values[0] = value;
event.timestamp = SystemClock.elapsedRealtimeNanos();
return event;
}
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 3326f80f5f91..677144c144a7 100644
--- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -27,8 +27,6 @@ import android.view.Display
import android.view.PointerIcon
import androidx.test.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -38,6 +36,7 @@ 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
@@ -45,8 +44,9 @@ 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}.
@@ -311,6 +311,18 @@ class InputManagerServiceTests {
assertTrue(service.getDeviceTypeAssociations().isEmpty())
}
+ @Test
+ fun testAddAndRemoveVirtualmKeyboardLayoutAssociation() {
+ val inputPort = "input port"
+ val languageTag = "language"
+ val layoutType = "layoutType"
+ localService.addKeyboardLayoutAssociation(inputPort, languageTag, layoutType)
+ verify(native).changeKeyboardLayoutAssociation()
+
+ localService.removeKeyboardLayoutAssociation(inputPort)
+ verify(native, times(2)).changeKeyboardLayoutAssociation()
+ }
+
private fun setVirtualMousePointerDisplayIdAndVerify(overrideDisplayId: Int) {
val thread = Thread { localService.setVirtualMousePointerDisplayId(overrideDisplayId) }
thread.start()
diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
index 54fa272dd3b6..d80aa5711199 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
@@ -22,8 +22,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -37,12 +37,10 @@ import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.ParceledListSlice;
import android.os.FileUtils;
import android.os.Looper;
import android.os.RemoteException;
@@ -103,7 +101,7 @@ public final class BackgroundInstallControlServiceTest {
@Mock
private Context mContext;
@Mock
- private IPackageManager mIPackageManager;
+ private PackageManager mPackageManager;
@Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
@@ -538,19 +536,19 @@ public final class BackgroundInstallControlServiceTest {
@Test
public void testHandleUsageEvent_packageAddedNoUsageEvent() throws
- RemoteException, NoSuchFieldException {
+ NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
InstallSourceInfo installSourceInfo = new InstallSourceInfo(
/* initiatingPackageName = */ null, /* initiatingPackageSigningInfo = */ null,
/* originatingPackageName = */ null,
/* installingPackageName = */ INSTALLER_NAME_1);
assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
- when(mIPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
+ when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mIPackageManager.getApplicationInfo(
+ when(mPackageManager.getApplicationInfoAsUser(
eq(PACKAGE_NAME_1),
- eq(0L),
+ any(),
anyInt())
).thenReturn(appInfo);
@@ -574,19 +572,19 @@ public final class BackgroundInstallControlServiceTest {
@Test
public void testHandleUsageEvent_packageAddedInsideTimeFrame() throws
- RemoteException, NoSuchFieldException {
+ NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
InstallSourceInfo installSourceInfo = new InstallSourceInfo(
/* initiatingPackageName = */ null, /* initiatingPackageSigningInfo = */ null,
/* originatingPackageName = */ null,
/* installingPackageName = */ INSTALLER_NAME_1);
assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
- when(mIPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
+ when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mIPackageManager.getApplicationInfo(
+ when(mPackageManager.getApplicationInfoAsUser(
eq(PACKAGE_NAME_1),
- eq(0L),
+ any(),
anyInt())
).thenReturn(appInfo);
@@ -618,19 +616,19 @@ public final class BackgroundInstallControlServiceTest {
@Test
public void testHandleUsageEvent_packageAddedOutsideTimeFrame1() throws
- RemoteException, NoSuchFieldException {
+ NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
InstallSourceInfo installSourceInfo = new InstallSourceInfo(
/* initiatingPackageName = */ null, /* initiatingPackageSigningInfo = */ null,
/* originatingPackageName = */ null,
/* installingPackageName = */ INSTALLER_NAME_1);
assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
- when(mIPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
+ when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mIPackageManager.getApplicationInfo(
+ when(mPackageManager.getApplicationInfoAsUser(
eq(PACKAGE_NAME_1),
- eq(0L),
+ any(),
anyInt())
).thenReturn(appInfo);
@@ -666,19 +664,19 @@ public final class BackgroundInstallControlServiceTest {
}
@Test
public void testHandleUsageEvent_packageAddedOutsideTimeFrame2() throws
- RemoteException, NoSuchFieldException {
+ NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
InstallSourceInfo installSourceInfo = new InstallSourceInfo(
/* initiatingPackageName = */ null, /* initiatingPackageSigningInfo = */ null,
/* originatingPackageName = */ null,
/* installingPackageName = */ INSTALLER_NAME_1);
assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
- when(mIPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
+ when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mIPackageManager.getApplicationInfo(
+ when(mPackageManager.getApplicationInfoAsUser(
eq(PACKAGE_NAME_1),
- eq(0L),
+ any(),
anyInt())
).thenReturn(appInfo);
@@ -760,8 +758,8 @@ public final class BackgroundInstallControlServiceTest {
packages.add(packageInfo2);
var packageInfo3 = makePackageInfo(PACKAGE_NAME_3);
packages.add(packageInfo3);
- doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
- anyLong(), anyInt());
+ doReturn(packages).when(mPackageManager).getInstalledPackagesAsUser(
+ any(), anyInt());
var resultPackages =
mBackgroundInstallControlService.getBackgroundInstalledPackages(0L, USER_ID_1);
@@ -808,8 +806,8 @@ public final class BackgroundInstallControlServiceTest {
}
@Override
- public IPackageManager getIPackageManager() {
- return mIPackageManager;
+ public PackageManager getPackageManager() {
+ return mPackageManager;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index 1f952c4014d0..9625188eb7aa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -62,12 +62,14 @@ public class UserManagerServiceUserPropertiesTest {
.setShowInSettings(45)
.setInheritDevicePolicy(67)
.setUseParentsContacts(false)
+ .setCrossProfileIntentFilterAccessControl(10)
.build();
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
actualProps.setShowInSettings(32);
actualProps.setInheritDevicePolicy(51);
actualProps.setUseParentsContacts(true);
+ actualProps.setCrossProfileIntentFilterAccessControl(20);
// Write the properties to xml.
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -150,6 +152,8 @@ public class UserManagerServiceUserPropertiesTest {
assertEqualGetterOrThrows(orig::getStartWithParent, copy::getStartWithParent, exposeAll);
assertEqualGetterOrThrows(orig::getInheritDevicePolicy,
copy::getInheritDevicePolicy, exposeAll);
+ assertEqualGetterOrThrows(orig::getCrossProfileIntentFilterAccessControl,
+ copy::getCrossProfileIntentFilterAccessControl, exposeAll);
// Items requiring hasManagePermission - put them here using hasManagePermission.
assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
@@ -203,5 +207,7 @@ public class UserManagerServiceUserPropertiesTest {
assertThat(expected.getShowInSettings()).isEqualTo(actual.getShowInSettings());
assertThat(expected.getInheritDevicePolicy()).isEqualTo(actual.getInheritDevicePolicy());
assertThat(expected.getUseParentsContacts()).isEqualTo(actual.getUseParentsContacts());
+ assertThat(expected.getCrossProfileIntentFilterAccessControl())
+ .isEqualTo(actual.getCrossProfileIntentFilterAccessControl());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index d7c1e3760ab2..1151222a7a90 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -84,7 +84,8 @@ public class UserManagerServiceUserTypeTest {
/* letsPersonalDataIntoProfile= */false).build());
final UserProperties.Builder userProps = new UserProperties.Builder()
.setShowInLauncher(17)
- .setUseParentsContacts(true);
+ .setUseParentsContacts(true)
+ .setCrossProfileIntentFilterAccessControl(10);
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
.setEnabled(1)
@@ -142,6 +143,8 @@ public class UserManagerServiceUserTypeTest {
assertEquals(17, type.getDefaultUserPropertiesReference().getShowInLauncher());
assertTrue(type.getDefaultUserPropertiesReference().getUseParentsContacts());
+ assertEquals(10, type.getDefaultUserPropertiesReference()
+ .getCrossProfileIntentFilterAccessControl());
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -185,6 +188,8 @@ public class UserManagerServiceUserTypeTest {
assertNotNull(props);
assertFalse(props.getStartWithParent());
assertFalse(props.getUseParentsContacts());
+ assertEquals(UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_ALL,
+ props.getCrossProfileIntentFilterAccessControl());
assertEquals(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT, props.getShowInLauncher());
assertFalse(type.hasBadge());
@@ -267,7 +272,8 @@ public class UserManagerServiceUserTypeTest {
final UserProperties.Builder props = new UserProperties.Builder()
.setShowInLauncher(19)
.setStartWithParent(true)
- .setUseParentsContacts(true);
+ .setUseParentsContacts(true)
+ .setCrossProfileIntentFilterAccessControl(10);
final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
builders.put(userTypeAosp1, new UserTypeDetails.Builder()
.setName(userTypeAosp1)
@@ -293,6 +299,8 @@ public class UserManagerServiceUserTypeTest {
assertEquals(Resources.ID_NULL, aospType.getIconBadge());
assertTrue(UserRestrictionsUtils.areEqual(restrictions, aospType.getDefaultRestrictions()));
assertEquals(19, aospType.getDefaultUserPropertiesReference().getShowInLauncher());
+ assertEquals(10, aospType.getDefaultUserPropertiesReference()
+ .getCrossProfileIntentFilterAccessControl());
assertTrue(aospType.getDefaultUserPropertiesReference().getStartWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference()
.getUseParentsContacts());
@@ -325,6 +333,8 @@ public class UserManagerServiceUserTypeTest {
makeRestrictionsBundle("no_remove_user", "no_bluetooth"),
aospType.getDefaultRestrictions()));
assertEquals(2020, aospType.getDefaultUserPropertiesReference().getShowInLauncher());
+ assertEquals(20, aospType.getDefaultUserPropertiesReference()
+ .getCrossProfileIntentFilterAccessControl());
assertFalse(aospType.getDefaultUserPropertiesReference().getStartWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference()
.getUseParentsContacts());
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 1b8d81d95281..810b294f27c3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -206,6 +206,8 @@ public final class UserManagerTest {
assertThat(typeProps.getShowInLauncher())
.isEqualTo(cloneUserProperties.getShowInLauncher());
assertThrows(SecurityException.class, cloneUserProperties::getStartWithParent);
+ assertThrows(SecurityException.class,
+ cloneUserProperties::getCrossProfileIntentFilterAccessControl);
// Verify clone user parent
assertThat(mUserManager.getProfileParent(primaryUserId)).isNull();
@@ -700,6 +702,7 @@ public final class UserManagerTest {
assertThat(userProps.getShowInLauncher()).isEqualTo(typeProps.getShowInLauncher());
assertThat(userProps.getShowInSettings()).isEqualTo(typeProps.getShowInSettings());
assertFalse(userProps.getUseParentsContacts());
+ assertThrows(SecurityException.class, userProps::getCrossProfileIntentFilterAccessControl);
assertThrows(SecurityException.class, userProps::getStartWithParent);
assertThrows(SecurityException.class, userProps::getInheritDevicePolicy);
}
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 fcdeb4d51070..19d8894c8d9c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -517,7 +517,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Mimic the behavior that display doesn't handle app's requested orientation.
final DisplayContent dc = activity.getTask().getDisplayContent();
doReturn(false).when(dc).onDescendantOrientationChanged(any());
- doReturn(false).when(dc).handlesOrientationChangeFromDescendant();
+ doReturn(false).when(dc).handlesOrientationChangeFromDescendant(anyInt());
final int requestedOrientation;
switch (newConfig.orientation) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index b87c5a364c82..10540dc5a9ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -18,6 +18,8 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -471,29 +473,28 @@ public class DisplayAreaTest extends WindowTestsBase {
}
@Test
- public void testSetIgnoreOrientationRequest() {
- final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test");
- final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
- spyOn(token);
- doReturn(mock(DisplayContent.class)).when(token).getDisplayContent();
- doNothing().when(token).setParent(any());
- final WindowState win = createWindowState(token);
- spyOn(win);
- doNothing().when(win).setParent(any());
- win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
- token.addChild(win, 0);
- area.addChild(token);
- doReturn(true).when(win).isVisible();
+ public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() {
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ final Task stack =
+ new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
+ final ActivityRecord activity = stack.getTopNonFinishingActivity();
- assertEquals(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, area.getOrientation());
+ tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- area.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSET, area.getOrientation());
+ verify(tda).onDescendantOrientationChanged(any());
+ verify(mDisplayContent, never()).onDescendantOrientationChanged(any());
+
+ tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+ verify(tda, times(2)).onDescendantOrientationChanged(any());
+ verify(mDisplayContent).onDescendantOrientationChanged(any());
}
@Test
- public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() {
+ public void testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedNoSensor() {
final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
final Task stack =
new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
@@ -501,20 +502,41 @@ public class DisplayAreaTest extends WindowTestsBase {
tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
verify(tda).onDescendantOrientationChanged(any());
- verify(mDisplayContent, never()).onDescendantOrientationChanged(any());
+ verify(mDisplayContent).onDescendantOrientationChanged(any());
tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
- activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
- verify(tda, times(2)).onDescendantOrientationChanged(any());
+ verify(tda).onDescendantOrientationChanged(any());
+ verify(mDisplayContent).onDescendantOrientationChanged(any());
+ }
+
+ @Test
+ public void testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedLocked() {
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ final Task stack =
+ new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
+ final ActivityRecord activity = stack.getTopNonFinishingActivity();
+
+ tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LOCKED);
+
+ verify(tda).onDescendantOrientationChanged(any());
+ verify(mDisplayContent).onDescendantOrientationChanged(any());
+
+ tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LOCKED);
+
+ verify(tda).onDescendantOrientationChanged(any());
verify(mDisplayContent).onDescendantOrientationChanged(any());
}
@Test
- public void testSetIgnoreOrientationRequest_updateOrientationRequestingTaskDisplayArea() {
+ public void testGetOrientationRequestingTaskDisplayArea_updateOrientationTaskDisplayArea() {
final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
final Task stack =
new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
@@ -526,7 +548,7 @@ public class DisplayAreaTest extends WindowTestsBase {
// TDA is no longer handling orientation request, clear the last focused TDA.
tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isNull();
+ assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda);
// TDA now handles orientation request, update last focused TDA based on the focused app.
tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
index 2ce1d6055d86..3c5bc1704954 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.mock;
import android.app.WindowConfiguration;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -227,7 +228,7 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
ArraySet<Integer> mRunningUids = new ArraySet<>();
@Override
- public boolean canActivityBeLaunched(@NonNull ActivityInfo activity,
+ public boolean canActivityBeLaunched(@NonNull ActivityInfo activity, Intent intent,
@WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
boolean isNewTask) {
return false;
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 75c5b6e13777..4eaae9fb8240 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -170,6 +172,30 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
}
@Test
+ public void testIgnoreOrientationRequest_displayReceiveOrientationChangeForNoSensor() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR);
+
+ verify(mFirstRoot).onDescendantOrientationChanged(any());
+ verify(mDisplay).onDescendantOrientationChanged(any());
+ }
+
+ @Test
+ public void testIgnoreOrientationRequest_displayReceiveOrientationChangeForLocked() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED);
+
+ verify(mFirstRoot).onDescendantOrientationChanged(any());
+ verify(mDisplay).onDescendantOrientationChanged(any());
+ }
+
+ @Test
public void testLaunchPortraitApp_fillsDisplayAreaGroup() {
mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -222,6 +248,21 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
}
@Test
+ public void testLaunchNoSensorApp_noSizeCompatAfterRotation() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR);
+ assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
+ assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
+
+ rotateDisplay(mDisplay, ROTATION_90);
+ assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
+ assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
+ }
+
+ @Test
public void testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup() {
mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -243,6 +284,26 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
}
@Test
+ public void testLaunchNoSensorApp_activityIsNotLetterboxForFixedOrientationDisplayAreaGroup() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR);
+ assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
+ }
+
+ @Test
+ public void testLaunchLockedApp_activityIsNotLetterboxForFixedOrientationInDisplayAreaGroup() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED);
+ assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
+ }
+
+ @Test
public void testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() {
mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -272,6 +333,20 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
}
@Test
+ public void testLaunchNoSensorApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR);
+
+ rotateDisplay(mDisplay, ROTATION_90);
+
+ assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
+ assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
+ }
+
+ @Test
public void testPlaceImeContainer_reparentToTargetDisplayAreaGroup() {
setupImeWindow();
final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 91f8d8d186c6..d43805ab9554 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -29,8 +29,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -422,24 +423,63 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
// Activity on TDA1 is focused
mDisplayContent.setFocusedApp(firstActivity);
- assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue();
- assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse();
+ final int testOrientation = SCREEN_ORIENTATION_PORTRAIT;
+
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
// No focused app, TDA1 is still recorded as last focused.
mDisplayContent.setFocusedApp(null);
- assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue();
- assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse();
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
// Activity on TDA2 is focused
mDisplayContent.setFocusedApp(secondActivity);
- assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse();
- assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue();
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue();
+ }
+
+ @Test
+ public void testCanSpecifyOrientation() {
+ final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+ FEATURE_VENDOR_FIRST);
+ final Task firstRootTask = firstTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final Task secondRootTask = secondTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+ .setTask(firstRootTask).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+ .setTask(secondRootTask).build();
+ firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
+
+ final int testOrientation = SCREEN_ORIENTATION_PORTRAIT;
+
+ // Activity on TDA1 is focused, but TDA1 cannot specify orientation because
+ // ignoreOrientationRequest is true
+ // Activity on TDA2 has ignoreOrientationRequest false but it doesn't have focus so it
+ // cannot specify orientation
+ mDisplayContent.setFocusedApp(firstActivity);
+
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
+
+ // Activity on TDA1 is not focused, and so it cannot specify orientation
+ // Activity on TDA2 is focused, and it can specify orientation because
+ // ignoreOrientationRequest is false
+ mDisplayContent.setFocusedApp(secondActivity);
+
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue();
}
@Test
- public void testIsLastFocused_onlyCountIfTaskDisplayAreaHandlesOrientationRequest() {
+ public void testCanSpecifyOrientationNoSensor() {
final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
@@ -455,34 +495,51 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
- // Activity on TDA1 is focused, but TDA1 doesn't respect orientation request
+ final int testOrientation = SCREEN_ORIENTATION_NOSENSOR;
+
+ // ignoreOrientationRequest is always false for SCREEN_ORIENTATION_NOSENSOR so
+ // only the TDAs with focus can specify orientations
mDisplayContent.setFocusedApp(firstActivity);
- assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse();
- assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse();
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
- // Activity on TDA2 is focused, and TDA2 respects orientation request
mDisplayContent.setFocusedApp(secondActivity);
- assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse();
- assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue();
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue();
}
@Test
- public void testIgnoreOrientationRequest() {
- final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
- final Task task = taskDisplayArea.createRootTask(
+ public void testCanSpecifyOrientationLocked() {
+ final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+ FEATURE_VENDOR_FIRST);
+ final Task firstRootTask = firstTaskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
+ final Task secondRootTask = secondTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+ .setTask(firstRootTask).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+ .setTask(secondRootTask).build();
+ firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
- mDisplayContent.setFocusedApp(activity);
- activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ final int testOrientation = SCREEN_ORIENTATION_LOCKED;
- assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+ // ignoreOrientationRequest is always false for SCREEN_ORIENTATION_NOSENSOR so
+ // only the TDAs with focus can specify orientations
+ mDisplayContent.setFocusedApp(firstActivity);
- taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
+
+ mDisplayContent.setFocusedApp(secondActivity);
- assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue();
}
@Test
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 d5c15794cc28..26fe5214a7ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -254,6 +254,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
+ assertTrue(mTaskFragment.mTaskFragmentVanishedSent);
assertTaskFragmentVanishedTransaction();
}
@@ -266,10 +267,12 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
+ assertTrue(mTaskFragment.mTaskFragmentVanishedSent);
assertTaskFragmentVanishedTransaction();
// Not trigger onTaskFragmentInfoChanged.
// Call onTaskFragmentAppeared before calling onTaskFragmentInfoChanged.
+ mTaskFragment.mTaskFragmentVanishedSent = false;
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
clearInvocations(mOrganizer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 19e3246cf02c..9cd80a34b714 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -57,6 +57,7 @@ import static org.junit.Assert.assertNull;
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.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
@@ -393,12 +394,16 @@ public class TaskTests extends WindowTestsBase {
leafTask1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_HOME);
leafTask2.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_STANDARD);
+ // We need to use an orientation that is not an exception for the
+ // ignoreOrientationRequest flag.
+ final int orientation = SCREEN_ORIENTATION_PORTRAIT;
+
assertEquals(leafTask2, rootTask.getTopChild());
- assertTrue(rootTask.handlesOrientationChangeFromDescendant());
+ assertTrue(rootTask.handlesOrientationChangeFromDescendant(orientation));
// Treat orientation request from home as handled.
- assertTrue(leafTask1.handlesOrientationChangeFromDescendant());
+ assertTrue(leafTask1.handlesOrientationChangeFromDescendant(orientation));
// Orientation request from standard activity in multi window will not be handled.
- assertFalse(leafTask2.handlesOrientationChangeFromDescendant());
+ assertFalse(leafTask2.handlesOrientationChangeFromDescendant(orientation));
}
@Test
@@ -655,7 +660,8 @@ public class TaskTests extends WindowTestsBase {
doReturn(parentWindowContainer).when(task).getParent();
doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea();
doReturn(rootTask).when(task).getRootTask();
- doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant();
+ doReturn(true).when(parentWindowContainer)
+ .handlesOrientationChangeFromDescendant(anyInt());
// Setting app to fixed portrait fits within parent, but Task shouldn't adjust the
// bounds because its parent says it will handle it at a later time.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 98a28cfbe22c..ebf316697d3a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -662,13 +662,13 @@ public class WindowContainerTests extends WindowTestsBase {
public void testSetOrientation() {
final TestWindowContainer root = spy(new TestWindowContainerBuilder(mWm).build());
final TestWindowContainer child = spy(root.addChildWindow());
- doReturn(true).when(root).handlesOrientationChangeFromDescendant();
+ doReturn(true).when(root).handlesOrientationChangeFromDescendant(anyInt());
child.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
child.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
// The ancestor should decide whether to dispatch the configuration change.
verify(child, never()).onConfigurationChanged(any());
- doReturn(false).when(root).handlesOrientationChangeFromDescendant();
+ doReturn(false).when(root).handlesOrientationChangeFromDescendant(anyInt());
child.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
// The ancestor doesn't handle the request so the descendant applies the change directly.
verify(child).onConfigurationChanged(any());
@@ -843,11 +843,14 @@ public class WindowContainerTests extends WindowTestsBase {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
final TestWindowContainer root = spy(builder.build());
+ // We use an orientation that is not an exception for the ignoreOrientationRequest flag
+ final int orientation = SCREEN_ORIENTATION_PORTRAIT;
+
final TestWindowContainer child = root.addChildWindow();
- assertFalse(child.handlesOrientationChangeFromDescendant());
+ assertFalse(child.handlesOrientationChangeFromDescendant(orientation));
- Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant();
- assertTrue(child.handlesOrientationChangeFromDescendant());
+ Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant(anyInt());
+ assertTrue(child.handlesOrientationChangeFromDescendant(orientation));
}
@Test
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
index a15417a1d588..2bddd74f9862 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
@@ -60,10 +60,10 @@ final class HotwordAudioStreamCopier {
private static final String OP_MESSAGE = "Streaming hotword audio to VoiceInteractionService";
private static final String TASK_ID_PREFIX = "HotwordDetectedResult@";
private static final String THREAD_NAME_PREFIX = "Copy-";
- private static final int DEFAULT_COPY_BUFFER_LENGTH_BYTES = 2_560;
// Corresponds to the OS pipe capacity in bytes
private static final int MAX_COPY_BUFFER_LENGTH_BYTES = 65_536;
+ private static final int DEFAULT_COPY_BUFFER_LENGTH_BYTES = 32_768;
private final AppOpsManager mAppOpsManager;
private final int mDetectorType;
@@ -109,6 +109,7 @@ final class HotwordAudioStreamCopier {
List<HotwordAudioStream> newAudioStreams = new ArrayList<>(audioStreams.size());
List<CopyTaskInfo> copyTaskInfos = new ArrayList<>(audioStreams.size());
int totalMetadataBundleSizeBytes = 0;
+ int totalInitialAudioSizeBytes = 0;
for (HotwordAudioStream audioStream : audioStreams) {
ParcelFileDescriptor[] clientPipe = ParcelFileDescriptor.createReliablePipe();
ParcelFileDescriptor clientAudioSource = clientPipe[0];
@@ -137,6 +138,10 @@ final class HotwordAudioStreamCopier {
}
}
+ // We are including the non-streamed initial audio
+ // (HotwordAudioStream.getInitialAudio()) bytes in the "stream" size metrics.
+ totalInitialAudioSizeBytes += audioStream.getInitialAudio().length;
+
ParcelFileDescriptor serviceAudioSource =
audioStream.getAudioStreamParcelFileDescriptor();
copyTaskInfos.add(new CopyTaskInfo(serviceAudioSource, clientAudioSink,
@@ -146,7 +151,7 @@ final class HotwordAudioStreamCopier {
String resultTaskId = TASK_ID_PREFIX + System.identityHashCode(result);
mExecutorService.execute(
new HotwordDetectedResultCopyTask(resultTaskId, copyTaskInfos,
- totalMetadataBundleSizeBytes));
+ totalMetadataBundleSizeBytes, totalInitialAudioSizeBytes));
return result.buildUpon().setAudioStreams(newAudioStreams).build();
}
@@ -167,13 +172,15 @@ final class HotwordAudioStreamCopier {
private final String mResultTaskId;
private final List<CopyTaskInfo> mCopyTaskInfos;
private final int mTotalMetadataSizeBytes;
+ private final int mTotalInitialAudioSizeBytes;
private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
HotwordDetectedResultCopyTask(String resultTaskId, List<CopyTaskInfo> copyTaskInfos,
- int totalMetadataSizeBytes) {
+ int totalMetadataSizeBytes, int totalInitialAudioSizeBytes) {
mResultTaskId = resultTaskId;
mCopyTaskInfos = copyTaskInfos;
mTotalMetadataSizeBytes = totalMetadataSizeBytes;
+ mTotalInitialAudioSizeBytes = totalInitialAudioSizeBytes;
}
@Override
@@ -195,25 +202,30 @@ final class HotwordAudioStreamCopier {
try {
HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType,
HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__STARTED,
- mVoiceInteractorUid, /* streamSizeBytes= */ 0, mTotalMetadataSizeBytes,
- size);
+ mVoiceInteractorUid, mTotalInitialAudioSizeBytes,
+ mTotalMetadataSizeBytes, size);
// TODO(b/244599891): Set timeout, close after inactivity
mExecutorService.invokeAll(tasks);
- int totalStreamSizeBytes = 0;
+ // We are including the non-streamed initial audio
+ // (HotwordAudioStream.getInitialAudio()) bytes in the "stream" size metrics.
+ int totalStreamSizeBytes = mTotalInitialAudioSizeBytes;
for (SingleAudioStreamCopyTask task : tasks) {
totalStreamSizeBytes += task.mTotalCopiedBytes;
}
- Slog.i(TAG, mResultTaskId + ": Task was completed. Total bytes streamed: "
- + totalStreamSizeBytes + ", total metadata bundle size bytes: "
+ Slog.i(TAG, mResultTaskId + ": Task was completed. Total bytes egressed: "
+ + totalStreamSizeBytes + " (including " + mTotalInitialAudioSizeBytes
+ + " bytes NOT streamed), total metadata bundle size bytes: "
+ mTotalMetadataSizeBytes);
HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType,
HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__ENDED,
mVoiceInteractorUid, totalStreamSizeBytes, mTotalMetadataSizeBytes,
size);
} catch (InterruptedException e) {
- int totalStreamSizeBytes = 0;
+ // We are including the non-streamed initial audio
+ // (HotwordAudioStream.getInitialAudio()) bytes in the "stream" size metrics.
+ int totalStreamSizeBytes = mTotalInitialAudioSizeBytes;
for (SingleAudioStreamCopyTask task : tasks) {
totalStreamSizeBytes += task.mTotalCopiedBytes;
}
@@ -222,8 +234,9 @@ final class HotwordAudioStreamCopier {
HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__INTERRUPTED_EXCEPTION,
mVoiceInteractorUid, totalStreamSizeBytes, mTotalMetadataSizeBytes,
size);
- Slog.e(TAG, mResultTaskId + ": Task was interrupted. Total bytes streamed: "
- + totalStreamSizeBytes + ", total metadata bundle size bytes: "
+ Slog.i(TAG, mResultTaskId + ": Task was interrupted. Total bytes egressed: "
+ + totalStreamSizeBytes + " (including " + mTotalInitialAudioSizeBytes
+ + " bytes NOT streamed), total metadata bundle size bytes: "
+ mTotalMetadataSizeBytes);
bestEffortPropagateError(e.getMessage());
} finally {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5d188b77c5fe..2a667e71632a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -388,6 +388,9 @@ public class TelephonyManager {
/** @hide */
public static final int INVALID_PORT_INDEX = -1;
+ /** @hide */
+ public static final String PROPERTY_ENABLE_NULL_CIPHER_TOGGLE = "enable_null_cipher_toggle";
+
private final Context mContext;
private final int mSubId;
@UnsupportedAppUsage
@@ -17933,4 +17936,111 @@ public class TelephonyManager {
ex.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns whether the domain selection service is supported.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}.
+ *
+ * @return {@code true} if the domain selection service is supported.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+ public boolean isDomainSelectionSupported() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isDomainSelectionSupported();
+ }
+ } catch (RemoteException ex) {
+ Rlog.w(TAG, "RemoteException", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the primary IMEI (International Mobile Equipment Identity) of the device as
+ * mentioned in GSMA TS.37. {@link #getImei(int)} returns the IMEI that belongs to the selected
+ * slotID whereas this API {@link #getPrimaryImei()} returns primary IMEI of the device.
+ * A single SIM device with only one IMEI will be set by default as primary IMEI.
+ * A multi-SIM device with multiple IMEIs will have one of the IMEIs set as primary as
+ * mentioned in GSMA TS37_2.2_REQ_8.
+ *
+ * <p>Requires one of the following permissions
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+ * active subscription.
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * <li>If the calling app has been granted the
+ * {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission.
+ * </ul>
+ *
+ * @return Primary IMEI of type string
+ * @throws UnsupportedOperationException if the radio doesn't support this feature.
+ * @throws SecurityException if the caller does not have the required permission/privileges
+ */
+ @NonNull
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
+ public String getPrimaryImei() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ Rlog.e(TAG, "getPrimaryImei(): IPhoneSubInfo instance is NULL");
+ throw new IllegalStateException("Telephony service not available.");
+ }
+ return telephony.getPrimaryImei(getOpPackageName(), getAttributionTag());
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getPrimaryImei() RemoteException : " + ex);
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Convert SIM state into string.
+ *
+ * @param state SIM state.
+ * @return SIM state in string format.
+ *
+ * @hide
+ */
+ @NonNull
+ public static String simStateToString(@SimState int state) {
+ switch (state) {
+ case TelephonyManager.SIM_STATE_UNKNOWN:
+ return "UNKNOWN";
+ case TelephonyManager.SIM_STATE_ABSENT:
+ return "ABSENT";
+ case TelephonyManager.SIM_STATE_PIN_REQUIRED:
+ return "PIN_REQUIRED";
+ case TelephonyManager.SIM_STATE_PUK_REQUIRED:
+ return "PUK_REQUIRED";
+ case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
+ return "NETWORK_LOCKED";
+ case TelephonyManager.SIM_STATE_READY:
+ return "READY";
+ case TelephonyManager.SIM_STATE_NOT_READY:
+ return "NOT_READY";
+ case TelephonyManager.SIM_STATE_PERM_DISABLED:
+ return "PERM_DISABLED";
+ case TelephonyManager.SIM_STATE_CARD_IO_ERROR:
+ return "CARD_IO_ERROR";
+ case TelephonyManager.SIM_STATE_CARD_RESTRICTED:
+ return "CARD_RESTRICTED";
+ case TelephonyManager.SIM_STATE_LOADED:
+ return "LOADED";
+ case TelephonyManager.SIM_STATE_PRESENT:
+ return "PRESENT";
+ default:
+ return "UNKNOWN(" + state + ")";
+ }
+ }
}
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index cdb7d7c9ff59..a2d20193a430 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -1570,8 +1570,8 @@ public class EuiccManager {
/**
* Returns whether the passing portIndex is available.
- * A port is available if it is active without an enabled profile on it or calling app can
- * activate a new profile on the selected port without any user interaction.
+ * A port is available if it is active without enabled profile on it or
+ * calling app has carrier privilege over the profile installed on the selected port.
* Always returns false if the cardId is a physical card.
*
* @param portIndex is an enumeration of the ports available on the UICC.
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index d3d94b424899..98ed1fa5ddeb 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -520,6 +520,14 @@ public class ImsCallSession {
@NonNull Set<RtpHeaderExtension> extensions) {
// no-op
}
+
+ /**
+ * Called when radio to send ANBRQ message to the access network to query the desired
+ * bitrate.
+ */
+ public void callSessionSendAnbrQuery(int mediaType, int direction, int bitsPerSecond) {
+ // no-op
+ }
}
private final IImsCallSession miSession;
@@ -1224,6 +1232,27 @@ public class ImsCallSession {
}
/**
+ * Deliver the bitrate for the indicated media type, direction and bitrate to the upper layer.
+ *
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond This value is the bitrate received from the NW through the Recommended
+ * bitrate MAC Control Element message and ImsStack converts this value from MAC bitrate
+ * to audio/video codec bitrate (defined in TS26.114).
+ */
+ public void callSessionNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.callSessionNotifyAnbr(mediaType, direction, bitsPerSecond);
+ } catch (RemoteException e) {
+ Log.e(TAG, "callSessionNotifyAnbr" + e);
+ }
+ }
+
+ /**
* A listener type for receiving notification on IMS call session events.
* When an event is generated for an {@link IImsCallSession},
* the application is notified by having one of the methods called on
@@ -1716,6 +1745,26 @@ public class ImsCallSession {
}
}, mListenerExecutor);
}
+
+ /**
+ * ANBR Query received.
+ *
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond This value is the bitrate requested by the other party UE through
+ * RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate
+ * (defined in TS36.321, range: 0 ~ 8000 kbit/s).
+ */
+ @Override
+ public void callSessionSendAnbrQuery(int mediaType, int direction,
+ int bitsPerSecond) {
+ Log.d(TAG, "callSessionSendAnbrQuery in ImsCallSession");
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionSendAnbrQuery(mediaType, direction, bitsPerSecond);
+ }
+ }, mListenerExecutor);
+ }
}
/**
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index db99acfd9a35..93cea254ccfc 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -16,6 +16,8 @@
package android.telephony.ims;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -25,9 +27,12 @@ import android.telephony.CallQuality;
import android.telephony.ServiceState;
import android.telephony.ims.aidl.IImsCallSessionListener;
import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.util.Log;
import com.android.ims.internal.IImsCallSession;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
@@ -44,7 +49,7 @@ import java.util.Set;
// ImsCallSessionListenerConverter is also changed.
@SystemApi
public class ImsCallSessionListener {
-
+ private static final String TAG = "ImsCallSessionListener";
private final IImsCallSessionListener mListener;
/** @hide */
@@ -808,5 +813,69 @@ public class ImsCallSessionListener {
e.rethrowFromSystemServer();
}
}
+
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ MEDIA_STREAM_TYPE_AUDIO,
+ MEDIA_STREAM_TYPE_VIDEO,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaStreamType {}
+
+ /**
+ * Media Stream Type - Audio
+ * @hide
+ */
+ public static final int MEDIA_STREAM_TYPE_AUDIO = 1;
+ /**
+ * Media Stream Type - Video
+ * @hide
+ */
+ public static final int MEDIA_STREAM_TYPE_VIDEO = 2;
+
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ MEDIA_STREAM_DIRECTION_UPLINK,
+ MEDIA_STREAM_DIRECTION_DOWNLINK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaStreamDirection {}
+
+ /**
+ * Media Stream Direction - Uplink
+ * @hide
+ */
+ public static final int MEDIA_STREAM_DIRECTION_UPLINK = 1;
+ /**
+ * Media Stream Direction - Downlink
+ * @hide
+ */
+ public static final int MEDIA_STREAM_DIRECTION_DOWNLINK = 2;
+
+ /**
+ * Access Network Bitrate Recommendation Query (ANBRQ), see 3GPP TS 26.114.
+ * This API triggers radio to send ANBRQ message to the access network to query the
+ * desired bitrate.
+ *
+ * @param mediaType {@link MediaStreamType} is used to identify media stream such as
+ * audio or video.
+ * @param direction {@link MediaStreamDirection} of this packet stream (e.g. uplink
+ * or downlink).
+ * @param bitsPerSecond This value is the bitrate requested by the other party UE through
+ * RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate
+ * (defined in TS36.321, range: 0 ~ 8000 kbit/s).
+ * @hide
+ */
+ public final void callSessionSendAnbrQuery(@MediaStreamType int mediaType,
+ @MediaStreamDirection int direction, @IntRange(from = 0) int bitsPerSecond) {
+ Log.d(TAG, "callSessionSendAnbrQuery in imscallsessonListener");
+ try {
+ mListener.callSessionSendAnbrQuery(mediaType, direction, bitsPerSecond);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
index b77d3063e2cc..50c438c7678e 100644
--- a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
@@ -65,6 +65,7 @@ public final class ImsRegistrationAttributes implements Parcelable {
public static final class Builder {
private final int mRegistrationTech;
private Set<String> mFeatureTags = Collections.emptySet();
+ private @Nullable SipDetails mSipDetails;
/**
* Build a new instance of {@link ImsRegistrationAttributes}.
@@ -100,13 +101,22 @@ public final class ImsRegistrationAttributes implements Parcelable {
}
/**
+ * Set the SIP information.
+ * @param details The SIP information related to this IMS registration.
+ */
+ public @NonNull Builder setSipDetails(@NonNull SipDetails details) {
+ mSipDetails = details;
+ return this;
+ }
+
+ /**
* @return A new instance created from this builder.
*/
public @NonNull ImsRegistrationAttributes build() {
return new ImsRegistrationAttributes(mRegistrationTech,
RegistrationManager.getAccessType(mRegistrationTech),
getAttributeFlags(mRegistrationTech),
- mFeatureTags);
+ mFeatureTags, mSipDetails);
}
/**
@@ -125,6 +135,28 @@ public final class ImsRegistrationAttributes implements Parcelable {
private final int mTransportType;
private final int mImsAttributeFlags;
private final ArrayList<String> mFeatureTags;
+ private final @Nullable SipDetails mSipDetails;
+ /**
+ * Create a new {@link ImsRegistrationAttributes} instance.
+ * This is for backward compatibility.
+ *
+ * @param registrationTech The technology that IMS has been registered on.
+ * @param transportType The transport type that IMS has been registered on.
+ * @param imsAttributeFlags The attributes associated with the IMS registration.
+ * @param featureTags The feature tags included in the IMS registration.
+ * @hide
+ */
+ public ImsRegistrationAttributes(
+ @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech,
+ @AccessNetworkConstants.TransportType int transportType,
+ @ImsAttributeFlag int imsAttributeFlags,
+ @Nullable Set<String> featureTags) {
+ mRegistrationTech = registrationTech;
+ mTransportType = transportType;
+ mImsAttributeFlags = imsAttributeFlags;
+ mFeatureTags = new ArrayList<>(featureTags);
+ mSipDetails = null;
+ }
/**
* Create a new {@link ImsRegistrationAttributes} instance.
@@ -133,6 +165,7 @@ public final class ImsRegistrationAttributes implements Parcelable {
* @param transportType The transport type that IMS has been registered on.
* @param imsAttributeFlags The attributes associated with the IMS registration.
* @param featureTags The feature tags included in the IMS registration.
+ * @param details The SIP information associated with the IMS registration.
* @see Builder
* @hide
*/
@@ -140,11 +173,13 @@ public final class ImsRegistrationAttributes implements Parcelable {
@ImsRegistrationImplBase.ImsRegistrationTech int registrationTech,
@AccessNetworkConstants.TransportType int transportType,
@ImsAttributeFlag int imsAttributeFlags,
- @Nullable Set<String> featureTags) {
+ @Nullable Set<String> featureTags,
+ @Nullable SipDetails details) {
mRegistrationTech = registrationTech;
mTransportType = transportType;
mImsAttributeFlags = imsAttributeFlags;
mFeatureTags = new ArrayList<>(featureTags);
+ mSipDetails = details;
}
/**@hide*/
@@ -154,6 +189,8 @@ public final class ImsRegistrationAttributes implements Parcelable {
mImsAttributeFlags = source.readInt();
mFeatureTags = new ArrayList<>();
source.readList(mFeatureTags, null /*classloader*/, java.lang.String.class);
+ mSipDetails = source.readParcelable(null /*loader*/,
+ android.telephony.ims.SipDetails.class);
}
/**
@@ -202,6 +239,13 @@ public final class ImsRegistrationAttributes implements Parcelable {
return new ArraySet<>(mFeatureTags);
}
+ /**
+ * @return The SIP information associated with the IMS registration.
+ */
+ public @Nullable SipDetails getSipDetails() {
+ return mSipDetails;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -213,6 +257,7 @@ public final class ImsRegistrationAttributes implements Parcelable {
dest.writeInt(mTransportType);
dest.writeInt(mImsAttributeFlags);
dest.writeList(mFeatureTags);
+ dest.writeParcelable(mSipDetails, flags);
}
public static final @NonNull Creator<ImsRegistrationAttributes> CREATOR =
@@ -236,17 +281,20 @@ public final class ImsRegistrationAttributes implements Parcelable {
return mRegistrationTech == that.mRegistrationTech
&& mTransportType == that.mTransportType
&& mImsAttributeFlags == that.mImsAttributeFlags
- && Objects.equals(mFeatureTags, that.mFeatureTags);
+ && Objects.equals(mFeatureTags, that.mFeatureTags)
+ && Objects.equals(mSipDetails, that.mSipDetails);
}
@Override
public int hashCode() {
- return Objects.hash(mRegistrationTech, mTransportType, mImsAttributeFlags, mFeatureTags);
+ return Objects.hash(mRegistrationTech, mTransportType, mImsAttributeFlags, mFeatureTags,
+ mSipDetails);
}
@Override
public String toString() {
return "ImsRegistrationAttributes { transportType= " + mTransportType + ", attributeFlags="
- + mImsAttributeFlags + ", featureTags=[" + mFeatureTags + "]}";
+ + mImsAttributeFlags + ", featureTags=[" + mFeatureTags + "]"
+ + ",SipDetails=" + mSipDetails + "}";
}
}
diff --git a/telephony/java/android/telephony/ims/PublishAttributes.aidl b/telephony/java/android/telephony/ims/PublishAttributes.aidl
new file mode 100644
index 000000000000..c569cd75eadd
--- /dev/null
+++ b/telephony/java/android/telephony/ims/PublishAttributes.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.ims;
+
+parcelable PublishAttributes;
diff --git a/telephony/java/android/telephony/ims/PublishAttributes.java b/telephony/java/android/telephony/ims/PublishAttributes.java
new file mode 100644
index 000000000000..c4ce6ed88a0d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/PublishAttributes.java
@@ -0,0 +1,180 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.RcsUceAdapter.PublishState;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * This class provides detailed information related to publish state, SIP information and
+ * presence tuples in publication.
+ * This allows the application can check the detailed information of publication.
+ * @hide
+ */
+@SystemApi
+public final class PublishAttributes implements Parcelable {
+
+ private final @PublishState int mPublishState;
+ private List<RcsContactPresenceTuple> mPresenceTuples;
+ private @Nullable SipDetails mSipDetails;
+
+ /**
+ * Builder for creating {@link Builder} instances.
+ * @hide
+ */
+ public static final class Builder {
+ private PublishAttributes mAttributes;
+ /**
+ * Build a new instance of {@link PublishAttributes}.
+ *
+ * @param publishState The current publication state {@link RcsUceAdapter.PublishState}.
+ */
+ public Builder(@PublishState int publishState) {
+ mAttributes = new PublishAttributes(publishState);
+ }
+
+ /**
+ * Sets the SIP information in received response to a publish operation.
+ * @param details The {@link SipDetails} in received response.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setSipDetails(@Nullable SipDetails details) {
+ mAttributes.mSipDetails = details;
+ return this;
+ }
+
+ /**
+ * The tuple elements associated with the presence element portion of the PIDF document
+ * successfully sent to the network.
+ * @param tuples The list of the {@link RcsContactPresenceTuple} sent to the server.
+ * The contact URI should not be included in this tuples.
+ * @return this The same instance of the builder.
+ */
+ public @NonNull Builder setPresenceTuples(@NonNull List<RcsContactPresenceTuple> tuples) {
+ mAttributes.mPresenceTuples = tuples;
+ return this;
+ }
+
+ /**
+ * @return a new PublishAttributes from this Builder.
+ */
+ public @NonNull PublishAttributes build() {
+ return mAttributes;
+ }
+ }
+
+ /**
+ * Generate the attributes related to the publication.
+ *
+ * @param publishState The current publication state.
+ * See {@link RcsUceAdapter.PublishState}.
+ */
+ private PublishAttributes(@PublishState int publishState) {
+ mPublishState = publishState;
+ }
+
+ /**
+ * @return The current publication state. See {@link RcsUceAdapter.PublishState}.
+ */
+ public int getPublishState() {
+ return mPublishState;
+ }
+
+ /**
+ * @return The list of the {@link RcsContactPresenceTuple} sent to the server.
+ */
+ public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() {
+ if (mPresenceTuples == null) {
+ return Collections.emptyList();
+ }
+ return mPresenceTuples;
+ }
+
+ /**
+ * @return The {@link SipDetails} received in response.
+ */
+ public @Nullable SipDetails getSipDetails() {
+ return mSipDetails;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPublishState);
+ dest.writeList(mPresenceTuples);
+ dest.writeParcelable(mSipDetails, 0);
+ }
+
+ public static final @NonNull Creator<PublishAttributes> CREATOR =
+ new Creator<PublishAttributes>() {
+ @Override
+ public PublishAttributes createFromParcel(Parcel source) {
+ return new PublishAttributes(source);
+ }
+
+ @Override
+ public PublishAttributes[] newArray(int size) {
+ return new PublishAttributes[size];
+ }
+ };
+
+ /**
+ * Construct a PublishAttributes object from the given parcel.
+ */
+ private PublishAttributes(Parcel in) {
+ mPublishState = in.readInt();
+ mPresenceTuples = new ArrayList<>();
+ in.readList(mPresenceTuples, null, RcsContactPresenceTuple.class);
+ mSipDetails = in.readParcelable(SipDetails.class.getClassLoader(),
+ android.telephony.ims.SipDetails.class);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PublishAttributes that = (PublishAttributes) o;
+ return mPublishState == that.mPublishState
+ && Objects.equals(mPresenceTuples, that.mPresenceTuples)
+ && Objects.equals(mSipDetails, that.mSipDetails);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPublishState, mPresenceTuples, mSipDetails);
+ }
+
+ @Override
+ public String toString() {
+ return "PublishAttributes { publishState= " + mPublishState
+ + ", presenceTuples=[" + mPresenceTuples + "]" + "SipDetails=" + mSipDetails + "}";
+ }
+}
+
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 91dc38ff9ddb..b49181e7367b 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
@@ -382,8 +383,21 @@ public class RcsUceAdapter {
/**
* Notifies the callback when the publish state has changed.
* @param publishState The latest update to the publish state.
+ *
+ * @deprecated Replaced by {@link #onPublishStateChange}, deprecated for
+ * sip information.
*/
+ @Deprecated
void onPublishStateChange(@PublishState int publishState);
+
+ /**
+ * Notifies the callback when the publish state has changed or the publish operation is
+ * done.
+ * @param attributes The latest information related to the publish.
+ */
+ default void onPublishStateChange(@NonNull PublishAttributes attributes) {
+ onPublishStateChange(attributes.getPublishState());
+ };
}
/**
@@ -404,13 +418,13 @@ public class RcsUceAdapter {
}
@Override
- public void onPublishStateChanged(int publishState) {
+ public void onPublishUpdated(@NonNull PublishAttributes attributes) {
if (mPublishStateChangeListener == null) return;
final long callingIdentity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() ->
- mPublishStateChangeListener.onPublishStateChange(publishState));
+ mPublishStateChangeListener.onPublishStateChange(attributes));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -459,7 +473,11 @@ public class RcsUceAdapter {
* The pending request has completed successfully due to all requested contacts information
* being delivered. The callback {@link #onCapabilitiesReceived(List)}
* for each contacts is required to be called before {@link #onComplete} is called.
+ *
+ * @deprecated Replaced by {@link #onComplete(SipDetails)}, deprecated for
+ * SIP information.
*/
+ @Deprecated
void onComplete();
/**
@@ -468,8 +486,36 @@ public class RcsUceAdapter {
* @param errorCode The reason for the framework being unable to process the request.
* @param retryIntervalMillis The time in milliseconds the requesting application should
* wait before retrying, if non-zero.
+ *
+ * @deprecated Replaced by {@link #onError(int, long, SipDetails)}, deprecated for
+ * SIP information.
*/
+ @Deprecated
void onError(@ErrorCode int errorCode, long retryIntervalMillis);
+
+ /**
+ * The pending request has completed successfully due to all requested contacts information
+ * being delivered. The callback {@link #onCapabilitiesReceived(List)}
+ * for each contacts is required to be called before {@link #onComplete} is called.
+ *
+ * @param details The SIP information related to this request.
+ */
+ default void onComplete(@Nullable SipDetails details) {
+ onComplete();
+ };
+
+ /**
+ * The pending request has resulted in an error and may need to be retried, depending on the
+ * error code.
+ * @param errorCode The reason for the framework being unable to process the request.
+ * @param retryIntervalMillis The time in milliseconds the requesting application should
+ * wait before retrying, if non-zero.
+ * @param details The SIP information related to this request.
+ */
+ default void onError(@ErrorCode int errorCode, long retryIntervalMillis,
+ @Nullable SipDetails details) {
+ onError(errorCode, retryIntervalMillis);
+ };
}
private final Context mContext;
@@ -554,19 +600,20 @@ public class RcsUceAdapter {
}
}
@Override
- public void onComplete() {
+ public void onComplete(@Nullable SipDetails details) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- executor.execute(() -> c.onComplete());
+ executor.execute(() -> c.onComplete(details));
} finally {
restoreCallingIdentity(callingIdentity);
}
}
@Override
- public void onError(int errorCode, long retryAfterMilliseconds) {
+ public void onError(int errorCode, long retryAfterMilliseconds,
+ @Nullable SipDetails details) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds));
+ executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds, details));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -650,19 +697,20 @@ public class RcsUceAdapter {
}
}
@Override
- public void onComplete() {
+ public void onComplete(@Nullable SipDetails details) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- executor.execute(() -> c.onComplete());
+ executor.execute(() -> c.onComplete(details));
} finally {
restoreCallingIdentity(callingIdentity);
}
}
@Override
- public void onError(int errorCode, long retryAfterMilliseconds) {
+ public void onError(int errorCode, long retryAfterMilliseconds,
+ @Nullable SipDetails details) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds));
+ executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds, details));
} finally {
restoreCallingIdentity(callingIdentity);
}
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 8d82282002a6..873ce6064cca 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -217,6 +217,23 @@ public interface RegistrationManager {
}
@Override
+ public void onDeregisteredWithDetails(ImsReasonInfo info,
+ @SuggestedAction int suggestedAction,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
+ @NonNull SipDetails details) {
+ if (mLocalCallback == null) return;
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onUnregistered(info, suggestedAction,
+ imsRadioTech));
+ mExecutor.execute(() -> mLocalCallback.onUnregistered(info, details));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
if (mLocalCallback == null) return;
@@ -314,6 +331,19 @@ public interface RegistrationManager {
}
/**
+ * Notifies the framework when the IMS Provider is unregistered from the IMS network.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ * @param details the {@link SipDetails} related to disconnected Ims registration.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void onUnregistered(@NonNull ImsReasonInfo info,
+ @NonNull SipDetails details) {
+ }
+
+ /**
* A failure has occurred when trying to handover registration to another technology type.
*
* @param imsTransportType The transport type that has failed to handover registration to.
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 94e9afbe9e38..25ebdd0b8b40 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -513,4 +513,69 @@ public class SipDelegateManager {
// ignore it
}
}
+
+ /**
+ * Register a new callback, which is used to notify the registrant of changes
+ * to the state of the Sip Sessions managed remotely by the IMS stack.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+ *
+ * @param executor the Executor that will be used to call the {@link SipDialogStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerSipDialogStateCallback(@NonNull Executor executor,
+ @NonNull SipDialogStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null SipDialogStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.attachExecutor(executor);
+ try {
+ IImsRcsController controller = mBinderCache.listenOnBinder(
+ callback, callback::binderDied);
+ if (controller == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ controller.registerSipDialogStateCallback(mSubId, callback.getCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterSipDialogStateCallback(@NonNull SipDialogStateCallback callback)
+ throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null SipDialogStateCallback.");
+
+ IImsRcsController controller = mBinderCache.removeRunnable(callback);
+ try {
+ if (controller == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ controller.unregisterSipDialogStateCallback(mSubId, callback.getCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/SipDetails.aidl b/telephony/java/android/telephony/ims/SipDetails.aidl
new file mode 100644
index 000000000000..476d806c881a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDetails.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.ims;
+
+parcelable SipDetails;
diff --git a/telephony/java/android/telephony/ims/SipDetails.java b/telephony/java/android/telephony/ims/SipDetails.java
new file mode 100644
index 000000000000..6ec5afb3046c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDetails.java
@@ -0,0 +1,290 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Contains the information for SIP.
+ */
+public final class SipDetails implements Parcelable {
+ /**
+ * Define a SIP method type related to this information.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "METHOD_", value = {
+ METHOD_REGISTER,
+ METHOD_PUBLISH,
+ METHOD_SUBSCRIBE
+ })
+ public @interface Method {}
+
+ /**
+ * Indicates information related to the SIP registration method.
+ * See RFC 3261 for details.
+ */
+ public static final int METHOD_REGISTER = 1;
+ /**
+ * Indicates information related to the SIP publication method.
+ * See RFC 3903 for details.
+ */
+ public static final int METHOD_PUBLISH = 2;
+ /**
+ * Indicates information related to the SIP subscription method.
+ * See RFC 3856 for details.
+ */
+ public static final int METHOD_SUBSCRIBE = 3;
+
+ /**
+ * Builder for creating {@link SipDetails} instances.
+ * @hide
+ */
+ public static final class Builder {
+ private int mMethod;
+ // Command Sequence value defined in RFC3261 Section 8.1.1.5
+ private int mCseq = 0;
+ private int mResponseCode = 0;
+ private String mResponsePhrase = "";
+ private int mReasonHeaderCause = 0;
+ private String mReasonHeaderText = "";
+ private @Nullable String mCallId;
+
+ /**
+ * Build a new instance of {@link SipDetails}.
+ *
+ * @param method Indicates which SIP method this information is for.
+ */
+ public Builder(@Method int method) {
+ this.mMethod = method;
+ }
+
+ /**
+ * Sets the value of the CSeq header field for this SIP method.
+ * The CSeq header field serves as a way to identify and order transactions.
+ * It consists of a sequence number and a method.
+ * The method MUST match that of the request.
+ * Ref RFC3261 Section 8.1.1.5.
+ * @param cSeq The value of the CSeq header field.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setCSeq(int cSeq) {
+ this.mCseq = cSeq;
+ return this;
+ }
+
+ /**
+ * Sets the sip response code and reason response for this SIP method.
+ * Ref RFC3261 Section 21.
+ * @param responseCode The SIP response code sent from the network for the
+ * operation token specified.
+ * @param responsePhrase The optional reason response from the network. If
+ * there is a reason header included in the response, that should take
+ * precedence over the reason provided in the status line. If the network
+ * provided no reason with the sip code, the string should be empty.
+ * @return The same instance of the builder.
+ */
+ public Builder setSipResponseCode(int responseCode,
+ @NonNull String responsePhrase) {
+ this.mResponseCode = responseCode;
+ this.mResponsePhrase = responsePhrase;
+ return this;
+ }
+
+
+ /**
+ * Sets the detailed information of reason header for this SIP method.
+ * Ref RFC3326.
+ * @param reasonHeaderCause The “cause” parameter of the “reason”
+ * header included in the SIP message.
+ * @param reasonHeaderText The “text” parameter of the “reason”
+ * header included in the SIP message.
+ * @return The same instance of the builder.
+ */
+ public Builder setSipResponseReasonHeader(int reasonHeaderCause,
+ @NonNull String reasonHeaderText) {
+ this.mReasonHeaderCause = reasonHeaderCause;
+ this.mReasonHeaderText = reasonHeaderText;
+ return this;
+ }
+
+
+ /**
+ * Sets the value of the Call-ID header field for this SIP method.
+ * Ref RFC3261 Section 8.1.1.4.
+ * @param callId The value of the Call-ID header field.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setCallId(@NonNull String callId) {
+ this.mCallId = callId;
+ return this;
+ }
+
+ /**
+ * @return a new SipDetails from this Builder.
+ */
+ public @NonNull SipDetails build() {
+ return new SipDetails(this);
+ }
+ }
+
+ private final int mMethod;
+ private final int mCseq;
+ private final int mResponseCode;
+ private final @NonNull String mResponsePhrase;
+ private final int mReasonHeaderCause;
+ private final @NonNull String mReasonHeaderText;
+ private final @Nullable String mCallId;
+
+ private SipDetails(Builder builder) {
+ mMethod = builder.mMethod;
+ mCseq = builder.mCseq;
+ mResponseCode = builder.mResponseCode;
+ mResponsePhrase = builder.mResponsePhrase;
+ mReasonHeaderCause = builder.mReasonHeaderCause;
+ mReasonHeaderText = builder.mReasonHeaderText;
+ mCallId = builder.mCallId;
+ }
+
+ /**
+ * @return The method type associated with this SIP information.
+ */
+ public int getMethod() {
+ return mMethod;
+ }
+
+ /**
+ * @return The command sequence value associated with this SIP information.
+ */
+ public int getCSeq() {
+ return mCseq;
+ }
+
+ /**
+ * @return The sip response code associated with this SIP information.
+ */
+ public int getResponseCode() {
+ return mResponseCode;
+ }
+
+ /**
+ * @return The optional reason response associated with this SIP information.
+ */
+ public @NonNull String getResponsePhrase() {
+ return mResponsePhrase;
+ }
+
+ /**
+ * @return The "cause" parameter of the reason header included in the SIP message
+ */
+ public int getReasonHeaderCause() {
+ return mReasonHeaderCause;
+ }
+
+ /**
+ * @return The "text" parameter of the reason header included in the SIP message.
+ */
+ public @NonNull String getReasonHeaderText() {
+ return mReasonHeaderText;
+ }
+
+ /**
+ * @return The Call-ID value associated with this SIP information.
+ */
+ public @Nullable String getCallId() {
+ return mCallId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mMethod);
+ dest.writeInt(mCseq);
+ dest.writeInt(mResponseCode);
+ dest.writeString(mResponsePhrase);
+ dest.writeInt(mReasonHeaderCause);
+ dest.writeString(mReasonHeaderText);
+ dest.writeString(mCallId);
+ }
+
+ public static final @NonNull Creator<SipDetails> CREATOR =
+ new Creator<SipDetails>() {
+ @Override
+ public SipDetails createFromParcel(Parcel source) {
+ return new SipDetails(source);
+ }
+
+ @Override
+ public SipDetails[] newArray(int size) {
+ return new SipDetails[size];
+ }
+ };
+
+ /**
+ * Construct a SipDetails object from the given parcel.
+ */
+ private SipDetails(Parcel in) {
+ mMethod = in.readInt();
+ mCseq = in.readInt();
+ mResponseCode = in.readInt();
+ mResponsePhrase = in.readString();
+ mReasonHeaderCause = in.readInt();
+ mReasonHeaderText = in.readString();
+ mCallId = in.readString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SipDetails that = (SipDetails) o;
+ return mMethod == that.mMethod
+ && mCseq == that.mCseq
+ && mResponseCode == that.mResponseCode
+ && TextUtils.equals(mResponsePhrase, that.mResponsePhrase)
+ && mReasonHeaderCause == that.mReasonHeaderCause
+ && TextUtils.equals(mReasonHeaderText, that.mReasonHeaderText)
+ && TextUtils.equals(mCallId, that.mCallId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMethod, mCseq, mResponseCode, mResponsePhrase, mReasonHeaderCause,
+ mReasonHeaderText, mCallId);
+ }
+
+ @Override
+ public String toString() {
+ return "SipDetails { methodType= " + mMethod + ", cSeq=" + mCseq
+ + ", ResponseCode=" + mResponseCode + ", ResponseCPhrase=" + mResponsePhrase
+ + ", ReasonHeaderCause=" + mReasonHeaderCause
+ + ", ReasonHeaderText=" + mReasonHeaderText + ", callId=" + mCallId + "}";
+ }
+}
diff --git a/telephony/java/android/telephony/ims/SipDialogState.aidl b/telephony/java/android/telephony/ims/SipDialogState.aidl
new file mode 100644
index 000000000000..0f084c744ce6
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDialogState.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.ims;
+
+parcelable SipDialogState; \ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/SipDialogState.java b/telephony/java/android/telephony/ims/SipDialogState.java
new file mode 100644
index 000000000000..815c59ae9b2c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDialogState.java
@@ -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 android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The state of an ongoing SIP dialog.
+ * @hide
+ */
+@SystemApi
+public final class SipDialogState implements Parcelable {
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "STATE_", value = {STATE_EARLY, STATE_CONFIRMED, STATE_CLOSED})
+ public @interface SipDialogStateCode {}
+ /**
+ * The device has sent out a dialog starting event and is awaiting a confirmation.
+ */
+ public static final int STATE_EARLY = 0;
+
+ /**
+ * The device has received a 2XX response to the early dialog.
+ */
+ public static final int STATE_CONFIRMED = 1;
+
+ /**
+ * The device has received either a 3XX+ response to a pending dialog request or a BYE
+ * request has been sent on this dialog.
+ */
+ public static final int STATE_CLOSED = 2;
+
+ private final int mState;
+
+ /**
+ * Builder for {@link SipDialogState}.
+ * @hide
+ */
+ public static final class Builder {
+ private int mState = STATE_EARLY;
+
+ /**
+ * constructor
+ * @param state The state of SipDialog
+ */
+ public Builder(@SipDialogStateCode int state) {
+ mState = state;
+ }
+
+ /**
+ * Build the {@link SipDialogState}.
+ * @return The {@link SipDialogState} instance.
+ */
+ public @NonNull SipDialogState build() {
+ return new SipDialogState(this);
+ }
+ }
+
+ /**
+ * set Dialog state
+ */
+ private SipDialogState(@NonNull Builder builder) {
+ this.mState = builder.mState;
+ }
+
+ private SipDialogState(Parcel in) {
+ mState = in.readInt();
+ }
+
+ /**
+ * @return The state of the SIP dialog
+ */
+ public @SipDialogStateCode int getState() {
+ return mState;
+ }
+
+ public static final @NonNull Creator<SipDialogState> CREATOR = new Creator<SipDialogState>() {
+ @Override
+ public SipDialogState createFromParcel(@NonNull Parcel in) {
+ return new SipDialogState(in);
+ }
+
+ @Override
+ public SipDialogState[] newArray(int size) {
+ return new SipDialogState[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mState);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SipDialogState sipDialog = (SipDialogState) o;
+
+ return mState == sipDialog.mState;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mState);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/SipDialogStateCallback.java b/telephony/java/android/telephony/ims/SipDialogStateCallback.java
new file mode 100644
index 000000000000..5730bac09d57
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDialogStateCallback.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.telephony.ims;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Binder;
+
+import com.android.internal.telephony.ISipDialogStateCallback;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * This callback is used to notify listeners of SIP Dialog state changes.
+ * @hide
+ */
+@SystemApi
+public abstract class SipDialogStateCallback {
+
+ private CallbackBinder mCallback;
+ /**
+ * @hide
+ */
+ public void attachExecutor(@NonNull @CallbackExecutor Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException("SipDialogStateCallback Executor must be non-null");
+ }
+ mCallback = new CallbackBinder(this, executor);
+ }
+
+ private static class CallbackBinder extends ISipDialogStateCallback.Stub {
+ private WeakReference<SipDialogStateCallback> mSipDialogStateCallbackWeakRef;
+ private Executor mExecutor;
+
+ private CallbackBinder(SipDialogStateCallback callback, Executor executor) {
+ mSipDialogStateCallbackWeakRef = new WeakReference<SipDialogStateCallback>(callback);
+ mExecutor = executor;
+ }
+
+ Executor getExecutor() {
+ return mExecutor;
+ }
+
+ @Override
+ public void onActiveSipDialogsChanged(List<SipDialogState> dialogs) {
+ SipDialogStateCallback callback = mSipDialogStateCallbackWeakRef.get();
+ if (callback == null || dialogs == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> callback.onActiveSipDialogsChanged(dialogs)));
+ }
+ }
+
+ /**
+ * The state of one or more SIP dialogs has changed.
+ *
+ * @param dialogs A List of SipDialogState objects representing the state of the active
+ * SIP Dialogs.
+ */
+ public abstract void onActiveSipDialogsChanged(@NonNull List<SipDialogState> dialogs);
+
+ /**
+ * An unexpected error has occurred and the Telephony process has crashed. This
+ * has caused this callback to be deregistered. The callback must be re-registered
+ * in order to continue listening to the IMS service state.
+ */
+ public abstract void onError();
+
+ /**
+ * The callback to notify the death of telephony process
+ * @hide
+ */
+ public final void binderDied() {
+ if (mCallback != null) {
+ mCallback.getExecutor().execute(() -> onError());
+ }
+ }
+
+ /**
+ * Return the callback binder
+ * @hide
+ */
+ public CallbackBinder getCallbackBinder() {
+ return mCallback;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
index c27fa4fc882d..0514df2cea10 100644
--- a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
@@ -23,6 +23,7 @@ import android.os.Binder;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.SipDetails;
import android.telephony.ims.stub.CapabilityExchangeEventListener;
import android.util.Log;
@@ -83,7 +84,11 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis
/**
* Receives the status of changes in the publishing connection from ims service
* and deliver this callback to the framework.
+ *
+ * @deprecated Replaced by {@link #onPublishUpdated(SipDetails)}, deprecated for
+ * sip information.
*/
+ @Deprecated
public void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
ICapabilityExchangeEventListener listener = mListenerBinder;
@@ -91,8 +96,29 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis
return;
}
try {
- listener.onPublishUpdated(reasonCode, reasonPhrase,
- reasonHeaderCause, reasonHeaderText);
+ SipDetails details = new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+ .setSipResponseCode(reasonCode, reasonPhrase)
+ .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+ .build();
+ listener.onPublishUpdated(details);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "onPublishUpdated exception: " + e);
+ throw new ImsException("Remote is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Receives the status of changes in the publishing connection from ims service
+ * and deliver this callback to the framework.
+ */
+ public void onPublishUpdated(@NonNull SipDetails details) throws ImsException {
+ ICapabilityExchangeEventListener listener = mListenerBinder;
+ if (listener == null) {
+ return;
+ }
+ try {
+ listener.onPublishUpdated(details);
} catch (RemoteException e) {
Log.w(LOG_TAG, "onPublishUpdated exception: " + e);
throw new ImsException("Remote is not available",
diff --git a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
index c675bc3204f6..21c838040dc7 100644
--- a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
@@ -18,7 +18,7 @@ package android.telephony.ims.aidl;
import android.net.Uri;
import android.telephony.ims.aidl.IOptionsRequestCallback;
-
+import android.telephony.ims.SipDetails;
import java.util.List;
/**
@@ -31,8 +31,7 @@ import java.util.List;
oneway interface ICapabilityExchangeEventListener {
void onRequestPublishCapabilities(int publishTriggerType);
void onUnpublish();
- void onPublishUpdated(int reasonCode, String reasonPhrase, int reasonHeaderCause,
- String reasonHeaderText);
+ void onPublishUpdated(in SipDetails details);
void onRemoteCapabilityRequest(in Uri contactUri,
in List<String> remoteCapabilities, IOptionsRequestCallback cb);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index ed0375251ffb..b58a5c79b76c 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -171,4 +171,17 @@ oneway interface IImsCallSessionListener {
* @param extensions the RTP header extensions received.
*/
void callSessionRtpHeaderExtensionsReceived(in List<RtpHeaderExtension> extensions);
+
+ /**
+ * Access Network Bitrate Recommendation Query (ANBRQ), see 3GPP TS 26.114.
+ * This API triggers radio to send ANBRQ message to the access network to query the desired
+ * bitrate.
+ *
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond This value is the bitrate requested by the other party UE
+ * through RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate
+ * (defined in TS36.321, range: 0 ~ 8000 kbit/s).
+ */
+ void callSessionSendAnbrQuery(int mediaType, int direction, int bitsPerSecond);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl
index 2c0dd8d9957a..b8701f15d0a8 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl
@@ -21,6 +21,8 @@ import android.os.Bundle;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.aidl.IImsTrafficSessionCallback;
+
import com.android.ims.internal.IImsCallSession;
/**
@@ -36,4 +38,8 @@ interface IImsMmTelListener {
oneway void onVoiceMessageCountUpdate(int count);
oneway void onAudioModeIsVoipChanged(int imsAudioHandler);
oneway void onTriggerEpsFallback(int reason);
+ oneway void onStartImsTrafficSession(int token, int trafficType, int accessNetworkType,
+ int trafficDirection, in IImsTrafficSessionCallback callback);
+ oneway void onModifyImsTrafficSession(int token, int accessNetworkType);
+ oneway void onStopImsTrafficSession(int token);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 8931a78709ed..8a4a79684b08 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -30,6 +30,7 @@ import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
import com.android.ims.ImsFeatureContainer;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ISipDialogStateCallback;
/**
* Interface used to interact with the Telephony IMS.
@@ -69,6 +70,10 @@ interface IImsRcsController {
void destroySipDelegate(int subId, ISipDelegate connection, int reason);
void triggerNetworkRegistration(int subId, ISipDelegate connection, int sipCode,
String sipReason);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
+ void registerSipDialogStateCallback(int subId, in ISipDialogStateCallback cb);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
+ void unregisterSipDialogStateCallback(int subId, in ISipDialogStateCallback cb);
// Internal commands that should not be made public
void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback);
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 32b22785cedc..0a5ea170320b 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -22,6 +22,7 @@ import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.SipDetails;
/**
* See {@link ImsManager#RegistrationCallback} for more information.
@@ -32,6 +33,7 @@ oneway interface IImsRegistrationCallback {
void onRegistered(in ImsRegistrationAttributes attr);
void onRegistering(in ImsRegistrationAttributes attr);
void onDeregistered(in ImsReasonInfo info, int suggestedAction, int imsRadioTech);
+ void onDeregisteredWithDetails(in ImsReasonInfo info, int suggestedAction, int imsRadioTech, in SipDetails detail);
void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
void onSubscriberAssociatedUriChanged(in Uri[] uris);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsTrafficSessionCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsTrafficSessionCallback.aidl
new file mode 100644
index 000000000000..05b97e5c78cf
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/IImsTrafficSessionCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.telephony.ims.aidl;
+
+import android.telephony.ims.feature.ConnectionFailureInfo;
+
+/**
+ * {@hide}
+ */
+oneway interface IImsTrafficSessionCallback {
+ void onReady();
+ void onError(in ConnectionFailureInfo info);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
index b99d8a7d6d38..cd08fcfce6c6 100644
--- a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
@@ -17,6 +17,7 @@
package android.telephony.ims.aidl;
import java.util.List;
+import android.telephony.ims.SipDetails;
/**
* Interface used by the framework to receive the response of the publish
@@ -25,6 +26,5 @@ import java.util.List;
*/
oneway interface IPublishResponseCallback {
void onCommandError(int code);
- void onNetworkResponse(int code, String reason);
- void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
+ void onNetworkResponse(in SipDetails details);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl
index 0f627b92a24c..acce7484b283 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl
@@ -17,7 +17,7 @@
package android.telephony.ims.aidl;
import android.telephony.ims.RcsContactUceCapability;
-
+import android.telephony.ims.SipDetails;
/**
* Provides interface for RCS UCE when receive a change.
*
@@ -25,6 +25,6 @@ import android.telephony.ims.RcsContactUceCapability;
*/
oneway interface IRcsUceControllerCallback {
void onCapabilitiesReceived(in List<RcsContactUceCapability> contactCapabilities);
- void onComplete();
- void onError(int errorCode, long retryAfterMilliseconds);
+ void onComplete(in SipDetails details);
+ void onError(int errorCode, long retryAfterMilliseconds, in SipDetails details);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl
index b6e84154f80f..e870c26eea5e 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl
@@ -16,11 +16,12 @@
package android.telephony.ims.aidl;
+import android.telephony.ims.PublishAttributes;
/**
* Interface for RCS UCE publish state change callbacks.
*
* {@hide}
*/
oneway interface IRcsUcePublishStateCallback {
- void onPublishStateChanged(int publishState);
+ void onPublishUpdated(in PublishAttributes attributes);
}
diff --git a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
index 8cc8020df29a..375ecd959f08 100644
--- a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
@@ -18,6 +18,7 @@ package android.telephony.ims.aidl;
import android.net.Uri;
import android.telephony.ims.RcsContactTerminatedReason;
+import android.telephony.ims.SipDetails;
import java.util.List;
import java.util.Map;
@@ -29,8 +30,7 @@ import java.util.Map;
*/
oneway interface ISubscribeResponseCallback {
void onCommandError(int code);
- void onNetworkResponse(int code, in String reason);
- void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
+ void onNetworkResponse(in SipDetails detail);
void onNotifyCapabilitiesUpdate(in List<String> pidfXmls);
void onResourceTerminated(in List<RcsContactTerminatedReason> uriTerminatedReason);
void onTerminated(in String reason, long retryAfterMilliseconds);
diff --git a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
index 65415ea441b5..40cb5ca54990 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
@@ -16,8 +16,10 @@
package android.telephony.ims.aidl;
+import android.annotation.NonNull;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
+import android.telephony.ims.SipDetails;
import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback;
/**
@@ -43,20 +45,38 @@ public class RcsPublishResponseAidlWrapper implements PublishResponseCallback {
}
@Override
- public void onNetworkResponse(int code, String reason) throws ImsException {
+ @Deprecated
+ public void onNetworkResponse(int code, String reasonPhrase) throws ImsException {
+ SipDetails details = new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+ .setSipResponseCode(code, reasonPhrase)
+ .build();
try {
- mResponseBinder.onNetworkResponse(code, reason);
+ mResponseBinder.onNetworkResponse(details);
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@Override
+ @Deprecated
public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
String reasonHeaderText) throws ImsException {
+ SipDetails details = new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+ .setSipResponseCode(code, reasonPhrase)
+ .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+ .build();
try {
- mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
- reasonHeaderText);
+ mResponseBinder.onNetworkResponse(details);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ @Override
+ public void onNetworkResponse(@NonNull SipDetails details)
+ throws ImsException {
+ try {
+ mResponseBinder.onNetworkResponse(details);
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
diff --git a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
index 11118c0617c2..2f54001b80ce 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
@@ -16,10 +16,12 @@
package android.telephony.ims.aidl;
+import android.annotation.NonNull;
import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
import android.telephony.ims.RcsContactTerminatedReason;
+import android.telephony.ims.SipDetails;
import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback;
import android.util.Pair;
@@ -49,20 +51,37 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac
}
@Override
- public void onNetworkResponse(int code, String reason) throws ImsException {
+ @Deprecated
+ public void onNetworkResponse(int code, String reasonPhrase) throws ImsException {
try {
- mResponseBinder.onNetworkResponse(code, reason);
+ mResponseBinder.onNetworkResponse(new SipDetails.Builder(
+ SipDetails.METHOD_SUBSCRIBE)
+ .setSipResponseCode(code, reasonPhrase)
+ .build());
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@Override
+ @Deprecated
public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
String reasonHeaderText) throws ImsException {
try {
- mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
- reasonHeaderText);
+ mResponseBinder.onNetworkResponse(new SipDetails.Builder(
+ SipDetails.METHOD_SUBSCRIBE)
+ .setSipResponseCode(code, reasonPhrase)
+ .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+ .build());
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ @Override
+ public void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+ try {
+ mResponseBinder.onNetworkResponse(details);
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index 8dcd711a96a9..798e8019502f 100755
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -417,6 +417,21 @@ public class ImsCallSessionImplBase extends IImsCallSession.Stub {
}
/**
+ * Deliver the bitrate for the indicated media type, direction and bitrate to the upper layer.
+ *
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond This value is the bitrate received from the NW through the Recommended
+ * bitrate MAC Control Element message and ImsStack converts this value from MAC bitrate
+ * to audio/video codec bitrate (defined in TS26.114).
+ * @hide
+ */
+ @Override
+ public void callSessionNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+ // no-op; not supported in compat layer.
+ }
+
+ /**
* There are two different ImsCallSessionListeners that need to reconciled here, we need to
* convert the "old" version of the com.android.ims.internal.IImsCallSessionListener to the
* "new" version of the Listener android.telephony.ims.ImsCallSessionListener when calling
@@ -662,5 +677,11 @@ public class ImsCallSessionImplBase extends IImsCallSession.Stub {
public void callQualityChanged(CallQuality callQuality) throws RemoteException {
mNewListener.callQualityChanged(callQuality);
}
+
+ @Override
+ public void callSessionSendAnbrQuery(int mediaType, int direction,
+ int bitsPerSecond) throws RemoteException {
+ mNewListener.callSessionSendAnbrQuery(mediaType, direction, bitsPerSecond);
+ }
}
}
diff --git a/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.aidl b/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.aidl
new file mode 100644
index 000000000000..d24e5807d51d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ 2
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.feature;
+
+parcelable ConnectionFailureInfo;
diff --git a/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java b/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java
new file mode 100644
index 000000000000..88d9aae3e19d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java
@@ -0,0 +1,185 @@
+/*
+ * 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.telephony.ims.feature;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides details on why transmitting IMS traffic failed.
+ *
+ * @hide
+ */
+public final class ConnectionFailureInfo implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = "REASON_",
+ value = {
+ REASON_NONE,
+ REASON_ACCESS_DENIED,
+ REASON_NAS_FAILURE,
+ REASON_RACH_FAILURE,
+ REASON_RLC_FAILURE,
+ REASON_RRC_REJECT,
+ REASON_RRC_TIMEOUT,
+ REASON_NO_SERVICE,
+ REASON_PDN_NOT_AVAILABLE,
+ REASON_RF_BUSY,
+ REASON_UNSPECIFIED
+ })
+ public @interface FailureReason {}
+
+ /** Default value */
+ public static final int REASON_NONE = 0;
+ /** Access class check failed */
+ public static final int REASON_ACCESS_DENIED = 1;
+ /** 3GPP Non-access stratum failure */
+ public static final int REASON_NAS_FAILURE = 2;
+ /** Random access failure */
+ public static final int REASON_RACH_FAILURE = 3;
+ /** Radio link failure */
+ public static final int REASON_RLC_FAILURE = 4;
+ /** Radio connection establishment rejected by network */
+ public static final int REASON_RRC_REJECT = 5;
+ /** Radio connection establishment timed out */
+ public static final int REASON_RRC_TIMEOUT = 6;
+ /** Device currently not in service */
+ public static final int REASON_NO_SERVICE = 7;
+ /** The PDN is no more active */
+ public static final int REASON_PDN_NOT_AVAILABLE = 8;
+ /** Radio resource is busy with another subscription */
+ public static final int REASON_RF_BUSY = 9;
+ /** Unspecified reason */
+ public static final int REASON_UNSPECIFIED = 0xFFFF;
+
+ private static final SparseArray<String> sReasonMap;
+ static {
+ sReasonMap = new SparseArray<>();
+ sReasonMap.set(REASON_NONE, "NONE");
+ sReasonMap.set(REASON_ACCESS_DENIED, "ACCESS_DENIED");
+ sReasonMap.set(REASON_NAS_FAILURE, "NAS_FAILURE");
+ sReasonMap.set(REASON_RACH_FAILURE, "RACH_FAILURE");
+ sReasonMap.set(REASON_RLC_FAILURE, "RLC_FAILURE");
+ sReasonMap.set(REASON_RRC_REJECT, "RRC_REJECT");
+ sReasonMap.set(REASON_RRC_TIMEOUT, "RRC_TIMEOUT");
+ sReasonMap.set(REASON_NO_SERVICE, "NO_SERVICE");
+ sReasonMap.set(REASON_PDN_NOT_AVAILABLE, "PDN_NOT_AVAILABLE");
+ sReasonMap.set(REASON_RF_BUSY, "RF_BUSY");
+ sReasonMap.set(REASON_UNSPECIFIED, "UNSPECIFIED");
+ }
+
+ /** The reason of failure */
+ private final @FailureReason int mReason;
+
+ /**
+ * Failure cause code from network or modem specific to the failure
+ *
+ * Reference: 3GPP TS 24.401 Annex A (Cause values for EPS mobility management)
+ * Reference: 3GPP TS 24.501 Annex A (Cause values for 5GS mobility management)
+ */
+ private final int mCauseCode;
+
+ /** Retry wait time provided by network in milliseconds */
+ private final int mWaitTimeMillis;
+
+ private ConnectionFailureInfo(Parcel in) {
+ mReason = in.readInt();
+ mCauseCode = in.readInt();
+ mWaitTimeMillis = in.readInt();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param reason The reason of failure.
+ * @param causeCode Failure cause code from network or modem specific to the failure.
+ * See 3GPP TS 24.401 Annex A (Cause values for EPS mobility management) and
+ * 3GPP TS 24.501 Annex A (Cause values for 5GS mobility management).
+ * @param waitTimeMillis Retry wait time provided by network in milliseconds.
+ * @hide
+ */
+ public ConnectionFailureInfo(@FailureReason int reason, int causeCode, int waitTimeMillis) {
+ mReason = reason;
+ mCauseCode = causeCode;
+ mWaitTimeMillis = waitTimeMillis;
+ }
+
+ /**
+ * @return the reason for the failure.
+ */
+ public @FailureReason int getReason() {
+ return mReason;
+ }
+
+ /**
+ * @return the cause code from the network or modem specific to the failure.
+ */
+ public int getCauseCode() {
+ return mCauseCode;
+ }
+
+ /**
+ * @return the retry wait time provided by the network in milliseconds.
+ */
+ public int getWaitTimeMillis() {
+ return mWaitTimeMillis;
+ }
+
+ /**
+ * @return the string format of {@link ConnectionFailureInfo}
+ */
+ @NonNull
+ @Override
+ public String toString() {
+ String reason = sReasonMap.get(mReason, "UNKNOWN");
+ return "ConnectionFailureInfo :: {" + mReason + " : " + reason + ", "
+ + mCauseCode + ", " + mWaitTimeMillis + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mReason);
+ out.writeInt(mCauseCode);
+ out.writeInt(mWaitTimeMillis);
+ }
+
+ public static final @NonNull Creator<ConnectionFailureInfo> CREATOR =
+ new Creator<ConnectionFailureInfo>() {
+ @Override
+ public ConnectionFailureInfo createFromParcel(Parcel in) {
+ return new ConnectionFailureInfo(in);
+ }
+
+ @Override
+ public ConnectionFailureInfo[] newArray(int size) {
+ return new ConnectionFailureInfo[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java b/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java
new file mode 100644
index 000000000000..245ee1511d98
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.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 android.telephony.ims.feature;
+
+import android.annotation.NonNull;
+
+/**
+ * A callback class used to receive the result of {@link MmTelFeature#startImsTrafficSession}.
+ * @hide
+ */
+public interface ImsTrafficSessionCallback {
+
+ /** The modem is ready to process the IMS traffic. */
+ void onReady();
+
+ /**
+ * Notifies that any IMS traffic is not sent to network due to any failure
+ * on cellular networks. IMS service shall call {@link MmTelFeature#stopImsTrafficSession()}
+ * when receiving this callback.
+ *
+ * @param info The information of the failure.
+ */
+ void onError(@NonNull ConnectionFailureInfo info);
+}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 53140ff70810..e21358832fbb 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -16,15 +16,18 @@
package android.telephony.ims.feature;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsCallSessionListener;
@@ -38,6 +41,7 @@ import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsMmTelListener;
import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.aidl.IImsTrafficSessionCallback;
import android.telephony.ims.aidl.ISrvccStartedCallback;
import android.telephony.ims.stub.ImsCallSessionImplBase;
import android.telephony.ims.stub.ImsEcbmImplBase;
@@ -56,6 +60,8 @@ import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CancellationException;
@@ -63,6 +69,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -78,6 +85,8 @@ public class MmTelFeature extends ImsFeature {
private static final String LOG_TAG = "MmTelFeature";
private Executor mExecutor;
+ private HashMap<ImsTrafficSessionCallback, ImsTrafficSessionCallbackWrapper> mTrafficCallbacks =
+ new HashMap<>();
/**
* @hide
*/
@@ -611,6 +620,167 @@ public class MmTelFeature extends ImsFeature {
public void onTriggerEpsFallback(@EpsFallbackReason int reason) {
}
+
+ /**
+ * Called when the IMS notifies the upcoming traffic type to the radio.
+ *
+ * @param token A nonce to identify the request
+ * @param trafficType The {@link ImsTrafficType} type for IMS traffic.
+ * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType}
+ * type of the radio access network.
+ * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+ * mobile terminated use case eg. MO/MT call/SMS etc.
+ * @param callback The callback to receive the result.
+ * @hide
+ */
+ @Override
+ public void onStartImsTrafficSession(int token,
+ @ImsTrafficType int trafficType,
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+ @ImsTrafficDirection int trafficDirection,
+ IImsTrafficSessionCallback callback) {
+
+ }
+
+ /**
+ * Called when the IMS notifies the traffic type has been stopped.
+ *
+ * @param token A nonce registered with {@link #onStartImsTrafficSession}.
+ * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType}
+ * type of the radio access network.
+ * @hide
+ */
+ @Override
+ public void onModifyImsTrafficSession(int token,
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType) {
+
+ }
+
+ /**
+ * Called when the IMS notifies the traffic type has been stopped.
+ *
+ * @param token A nonce registered with {@link #onStartImsTrafficSession}.
+ * @hide
+ */
+ @Override
+ public void onStopImsTrafficSession(int token) {
+
+ }
+ }
+
+ /**
+ * A wrapper class of {@link ImsTrafficSessionCallback}.
+ * @hide
+ */
+ public static class ImsTrafficSessionCallbackWrapper {
+ public static final int INVALID_TOKEN = -1;
+
+ private static final int MAX_TOKEN = 0x10000;
+
+ private static final AtomicInteger sTokenGenerator = new AtomicInteger();
+
+ /** Callback to receive the response */
+ private IImsTrafficSessionCallbackStub mCallback = null;
+ /** Identifier to distinguish each IMS traffic request */
+ private int mToken = INVALID_TOKEN;
+
+ private ImsTrafficSessionCallback mImsTrafficSessionCallback;
+
+ private ImsTrafficSessionCallbackWrapper(ImsTrafficSessionCallback callback) {
+ mImsTrafficSessionCallback = callback;
+ }
+
+ /**
+ * Updates the callback.
+ *
+ * The mToken should be kept since it is used to identify the traffic notified to the modem
+ * until calling {@link MmtelFEature#stopImsTrafficSession}.
+ */
+ final void update(@NonNull @CallbackExecutor Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException(
+ "ImsTrafficSessionCallback Executor must be non-null");
+ }
+
+ if (mCallback == null) {
+ // initial start of Ims traffic.
+ mCallback = new IImsTrafficSessionCallbackStub(
+ mImsTrafficSessionCallback, executor);
+ mToken = generateToken();
+ } else {
+ // handover between cellular and Wi-Fi
+ mCallback.update(executor);
+ }
+ }
+
+ /**
+ * Using a static class and weak reference here to avoid memory leak caused by the
+ * {@link IImsTrafficSessionCallback.Stub} callback retaining references to the outside
+ * {@link ImsTrafficSessionCallback}.
+ */
+ private static class IImsTrafficSessionCallbackStub
+ extends IImsTrafficSessionCallback.Stub {
+ private WeakReference<ImsTrafficSessionCallback> mImsTrafficSessionCallbackWeakRef;
+ private Executor mExecutor;
+
+ IImsTrafficSessionCallbackStub(ImsTrafficSessionCallback imsTrafficCallback,
+ Executor executor) {
+ mImsTrafficSessionCallbackWeakRef =
+ new WeakReference<ImsTrafficSessionCallback>(imsTrafficCallback);
+ mExecutor = executor;
+ }
+
+ void update(Executor executor) {
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onReady() {
+ ImsTrafficSessionCallback callback = mImsTrafficSessionCallbackWeakRef.get();
+ if (callback == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> callback.onReady()));
+ }
+
+ @Override
+ public void onError(ConnectionFailureInfo info) {
+ ImsTrafficSessionCallback callback = mImsTrafficSessionCallbackWeakRef.get();
+ if (callback == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> callback.onError(info)));
+ }
+ }
+
+ /**
+ * Returns the callback binder.
+ */
+ final IImsTrafficSessionCallbackStub getCallbackBinder() {
+ return mCallback;
+ }
+
+ /**
+ * Returns the token.
+ */
+ final int getToken() {
+ return mToken;
+ }
+
+ /**
+ * Resets the members.
+ * It's called by {@link MmTelFeature#stopImsTrafficSession}.
+ */
+ final void reset() {
+ mCallback = null;
+ mToken = INVALID_TOKEN;
+ }
+
+ private static int generateToken() {
+ int token = sTokenGenerator.incrementAndGet();
+ if (token == MAX_TOKEN) sTokenGenerator.set(0);
+ return token;
+ }
}
/**
@@ -725,6 +895,81 @@ public class MmTelFeature extends ImsFeature {
*/
public static final int EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE = 2;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = "IMS_TRAFFIC_TYPE_",
+ value = {
+ IMS_TRAFFIC_TYPE_NONE,
+ IMS_TRAFFIC_TYPE_EMERGENCY,
+ IMS_TRAFFIC_TYPE_EMERGENCY_SMS,
+ IMS_TRAFFIC_TYPE_VOICE,
+ IMS_TRAFFIC_TYPE_VIDEO,
+ IMS_TRAFFIC_TYPE_SMS,
+ IMS_TRAFFIC_TYPE_REGISTRATION,
+ IMS_TRAFFIC_TYPE_UT_XCAP
+ })
+ public @interface ImsTrafficType {}
+
+ /**
+ * Default value for initialization. Internal use only.
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_TYPE_NONE = -1;
+ /**
+ * Emergency call
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_TYPE_EMERGENCY = 0;
+ /**
+ * Emergency SMS
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_TYPE_EMERGENCY_SMS = 1;
+ /**
+ * Voice call
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_TYPE_VOICE = 2;
+ /**
+ * Video call
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_TYPE_VIDEO = 3;
+ /**
+ * SMS over IMS
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_TYPE_SMS = 4;
+ /**
+ * IMS registration and subscription for reg event package (signaling)
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_TYPE_REGISTRATION = 5;
+ /**
+ * Ut/XCAP (XML Configuration Access Protocol)
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_TYPE_UT_XCAP = 6;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "IMS_TRAFFIC_DIRECTION_" },
+ value = {IMS_TRAFFIC_DIRECTION_INCOMING, IMS_TRAFFIC_DIRECTION_OUTGOING})
+ public @interface ImsTrafficDirection {}
+
+ /**
+ * Indicates that the traffic is an incoming traffic.
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_DIRECTION_INCOMING = 0;
+ /**
+ * Indicates that the traffic is an outgoing traffic.
+ * @hide
+ */
+ public static final int IMS_TRAFFIC_DIRECTION_OUTGOING = 1;
+
private IImsMmTelListener mListener;
/**
@@ -950,6 +1195,136 @@ public class MmTelFeature extends ImsFeature {
}
/**
+ * Starts a new IMS traffic session with the framework.
+ *
+ * This API notifies the NAS and RRC layers of the modem that IMS traffic of type
+ * {@link ImsTrafficType} is starting for the IMS session represented by a
+ * {@link ImsTrafficSessionCallback}. The {@link ImsTrafficSessionCallback}
+ * will notify the caller when IMS traffic is ready to start via the
+ * {@link ImsTrafficSessionCallback#onReady()} callback. If there was an error starting
+ * IMS traffic for the specified traffic type, {@link ImsTrafficSessionCallback#onError()} will
+ * be called, which will also notify the caller of the reason of the failure.
+ *
+ * If there is a handover that changes the {@link AccessNetworkConstants#RadioAccessNetworkType}
+ * of this IMS traffic session, then {@link #modifyImsTrafficSession} should be called. This is
+ * used, for example, when a WiFi <-> cellular handover occurs.
+ *
+ * Once the IMS traffic session is finished, {@link #stopImsTrafficSession} must be called.
+ *
+ * Note: This API will be used to prioritize RF resources in case of DSDS. The service priority
+ * is EMERGENCY > EMERGENCY SMS > VOICE > VIDEO > SMS > REGISTRATION > Ut/XCAP. RF
+ * shall be prioritized to the subscription which handles the higher priority service.
+ * When both subscriptions are handling the same type of service, then RF shall be
+ * prioritized to the voice preferred sub.
+ *
+ * @param trafficType The {@link ImsTrafficType} type for IMS traffic.
+ * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType} type of
+ * the radio access network.
+ * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+ * mobile terminated use case eg. MO/MT call/SMS etc.
+ * @param executor The Executor that will be used to call the {@link ImsTrafficSessionCallback}.
+ * @param callback The session representing the IMS Session associated with a specific
+ * trafficType. This callback instance should only be used for the specified traffic type
+ * until {@link #stopImsTrafficSession} is called.
+ *
+ * @see modifyImsTrafficSession
+ * @see stopImsTrafficSession
+ *
+ * @hide
+ */
+ public final void startImsTrafficSession(@ImsTrafficType int trafficType,
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+ @ImsTrafficDirection int trafficDirection,
+ @NonNull Executor executor, @NonNull ImsTrafficSessionCallback callback) {
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ // TODO: retrieve from the callback list
+ ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
+ if (callbackWrapper == null) {
+ callbackWrapper = new ImsTrafficSessionCallbackWrapper(callback);
+ mTrafficCallbacks.put(callback, callbackWrapper);
+ }
+ try {
+ callbackWrapper.update(executor);
+ listener.onStartImsTrafficSession(callbackWrapper.getToken(),
+ trafficType, accessNetworkType, trafficDirection,
+ callbackWrapper.getCallbackBinder());
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Modifies an existing IMS traffic session represented by the associated
+ * {@link ImsTrafficSessionCallback}.
+ *
+ * The {@link ImsTrafficSessionCallback} will notify the caller when IMS traffic is ready to
+ * start after modification using the {@link ImsTrafficSessionCallback#onReady()} callback.
+ * If there was an error modifying IMS traffic for the new radio access network type type,
+ * {@link ImsTrafficSessionCallback#onError()} will be called, which will also notify the
+ * caller of the reason of the failure.
+ *
+ * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType} type of
+ * the radio access network.
+ * @param callback The callback registered with {@link #startImsTrafficSession}.
+ *
+ * @see startImsTrafficSession
+ * @see stopImsTrafficSession
+ *
+ * @hide
+ */
+ public final void modifyImsTrafficSession(
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+ @NonNull ImsTrafficSessionCallback callback) {
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
+ if (callbackWrapper == null) {
+ // should not reach here.
+ throw new IllegalStateException("Unknown ImsTrafficSessionCallback instance.");
+ }
+ try {
+ listener.onModifyImsTrafficSession(callbackWrapper.getToken(), accessNetworkType);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notifies the framework that the IMS traffic session represented by the associated
+ * {@link ImsTrafficSessionCallback} has ended.
+ *
+ * @param callback The callback registered with {@link #startImsTrafficSession}.
+ *
+ * @see startImsTrafficSession
+ * @see modifyImsTrafficSession
+ *
+ * @hide
+ */
+ public final void stopImsTrafficSession(@NonNull ImsTrafficSessionCallback callback) {
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
+ if (callbackWrapper == null) {
+ // should not reach here.
+ throw new IllegalStateException("Unknown ImsTrafficSessionCallback instance.");
+ }
+ try {
+ listener.onStopImsTrafficSession(callbackWrapper.getToken());
+ callbackWrapper.reset();
+ mTrafficCallbacks.remove(callback);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Provides the MmTelFeature with the ability to return the framework Capability Configuration
* for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
* includes a capability A to enable or disable, this method should return the correct enabled
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index 7a1a2af060d2..4070beda3486 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -23,6 +23,7 @@ import android.net.Uri;
import android.telephony.ims.ImsException;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.SipDetails;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
@@ -108,12 +109,31 @@ public interface CapabilityExchangeEventListener {
* the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
* cases when the Telephony stack has crashed.
*
+ * @deprecated Replaced sip information with the newly added
+ * {@link #onPublishUpdated(SipDetails)}.
*/
+ @Deprecated
default void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
}
/**
+ * Notify the framework that the ImsService has refreshed the PUBLISH
+ * internally, which has resulted in a new PUBLISH result.
+ * <p>
+ * This method must be called to notify the framework of SUCCESS (200 OK) and FAILURE (300+)
+ * codes in order to keep the AOSP stack up to date.
+ * @param details The SIP information received in response to a publish operation.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
+ * currently connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+ * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
+ * cases when the Telephony stack has crashed.
+ */
+ default void onPublishUpdated(@NonNull SipDetails details)
+ throws ImsException {
+ }
+ /**
* Inform the framework of an OPTIONS query from a remote device for this device's UCE
* capabilities.
* <p>
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index f46938af2533..e46351dcf820 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -16,6 +16,8 @@
package android.telephony.ims.stub;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Bundle;
@@ -37,6 +39,8 @@ import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
import com.android.internal.telephony.util.TelephonyUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CancellationException;
@@ -68,6 +72,46 @@ public class ImsCallSessionImplBase implements AutoCloseable {
*/
public static final int USSD_MODE_REQUEST = 1;
+ /** @hide */
+ @IntDef(
+ value = {
+ MEDIA_STREAM_TYPE_AUDIO,
+ MEDIA_STREAM_TYPE_VIDEO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaStreamType {}
+
+ /**
+ * Media Stream Type - Audio
+ * @hide
+ */
+ public static final int MEDIA_STREAM_TYPE_AUDIO = 1;
+ /**
+ * Media Stream Type - Video
+ * @hide
+ */
+ public static final int MEDIA_STREAM_TYPE_VIDEO = 2;
+
+ /** @hide */
+ @IntDef(
+ value = {
+ MEDIA_STREAM_DIRECTION_UPLINK,
+ MEDIA_STREAM_DIRECTION_DOWNLINK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaStreamDirection {}
+
+ /**
+ * Media Stream Direction - Uplink
+ * @hide
+ */
+ public static final int MEDIA_STREAM_DIRECTION_UPLINK = 1;
+ /**
+ * Media Stream Direction - Downlink
+ * @hide
+ */
+ public static final int MEDIA_STREAM_DIRECTION_DOWNLINK = 2;
+
/**
* Defines IMS call session state.
*/
@@ -327,6 +371,12 @@ public class ImsCallSessionImplBase implements AutoCloseable {
new ArraySet<RtpHeaderExtension>(extensions)), "sendRtpHeaderExtensions");
}
+ @Override
+ public void callSessionNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.callSessionNotifyAnbr(
+ mediaType, direction, bitsPerSecond), "callSessionNotifyAnbr");
+ }
+
// Call the methods with a clean calling identity on the executor and wait indefinitely for
// the future to return.
private void executeMethodAsync(Runnable r, String errorLogName) {
@@ -730,6 +780,22 @@ public class ImsCallSessionImplBase implements AutoCloseable {
public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
}
+ /**
+ * Deliver the bitrate for the indicated media type, direction and bitrate to the upper layer.
+ *
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond This value is the bitrate received from the NW through the Recommended
+ * bitrate MAC Control Element message and ImsStack converts this value from MAC bitrate
+ * to audio/video codec bitrate (defined in TS26.114).
+ * @hide
+ */
+ public void callSessionNotifyAnbr(@MediaStreamType int mediaType,
+ @MediaStreamDirection int direction, @IntRange(from = 0) int bitsPerSecond) {
+ // Base Implementation - Should be overridden by IMS service
+ Log.i(LOG_TAG, "ImsCallSessionImplBase callSessionNotifyAnbr - mediaType: " + mediaType);
+ }
+
/** @hide */
public IImsCallSession getServiceImpl() {
return mServiceImpl;
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 916e1e98a4fe..fa4c9b23ae78 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -26,6 +26,7 @@ import android.os.RemoteException;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.SipDetails;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
@@ -516,6 +517,68 @@ public class ImsRegistrationImplBase {
}
/**
+ * Notify the framework that the device is disconnected from the IMS network.
+ * <p>
+ * Note: Before calling {@link #onDeregistered(ImsReasonInfo, SipDetails)}, ImsService should
+ * ensure that any changes to {@link android.telephony.ims.feature.ImsFeature} capability
+ * availability is sent to the framework.
+ * For example,
+ * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
+ * and
+ * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
+ * may be set to unavailable to ensure the framework knows these services are no longer
+ * available due to de-registration. If ImsService do not report capability changes impacted
+ * by de-registration, the framework will not know which features are no longer available as a
+ * result.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ * @param details the {@link SipDetails} related to disconnected Ims registration
+ * @hide This API is not part of the Android public SDK API
+ */
+ @SystemApi
+ public final void onDeregistered(@Nullable ImsReasonInfo info,
+ @NonNull SipDetails details) {
+ onDeregistered(info, RegistrationManager.SUGGESTED_ACTION_NONE, REGISTRATION_TECH_NONE,
+ details);
+ }
+
+ /**
+ * Notify the framework that the device is disconnected from the IMS network.
+ * <p>
+ * Note: Before calling {@link #onDeregistered(ImsReasonInfo, SipDetails)}, ImsService should
+ * ensure that any changes to {@link android.telephony.ims.feature.ImsFeature} capability
+ * availability is sent to the framework.
+ * For example,
+ * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
+ * and
+ * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
+ * may be set to unavailable to ensure the framework knows these services are no longer
+ * available due to de-registration. If ImsService do not report capability changes impacted
+ * by de-registration, the framework will not know which features are no longer available as a
+ * result.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ * @param suggestedAction the expected behavior of radio protocol stack.
+ * @param details the {@link SipDetails} related to disconnected Ims registration
+ * @hide This API is not part of the Android public SDK API
+ */
+ @SystemApi
+ public final void onDeregistered(@Nullable ImsReasonInfo info,
+ @RegistrationManager.SuggestedAction int suggestedAction,
+ @ImsRegistrationTech int imsRadioTech, @NonNull SipDetails details) {
+ updateToDisconnectedState(info, suggestedAction, imsRadioTech);
+ // ImsReasonInfo should never be null.
+ final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
+ mCallbacks.broadcastAction((c) -> {
+ try {
+ c.onDeregisteredWithDetails(reasonInfo, suggestedAction, imsRadioTech, details);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback.");
+ }
+ });
+ }
+
+ /**
* Notify the framework that the handover from the current radio technology to the technology
* defined in {@code imsRadioTech} has failed.
* @param imsRadioTech The technology that has failed to be changed. Valid values are
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 474eb05ca19c..3e1c9ef287c6 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -24,6 +24,7 @@ import android.annotation.SystemApi;
import android.net.Uri;
import android.telephony.ims.ImsException;
import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.SipDetails;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
@@ -155,7 +156,11 @@ public class RcsCapabilityExchangeImplBase {
* is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
* the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
* when the Telephony stack has crashed.
+ *
+ * @deprecated Replaced sip information with the newly added
+ * {@link #onNetworkResponse(SipDetails)}.
*/
+ @Deprecated
void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
@NonNull String reason) throws ImsException;
@@ -178,11 +183,29 @@ public class RcsCapabilityExchangeImplBase {
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
* the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
* rare cases when the Telephony stack has crashed.
+ *
+ * @deprecated Replaced sip information with the newly added
+ * {@link #onNetworkResponse(SipDetails)}.
*/
+ @Deprecated
void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
@NonNull String reasonPhrase,
@IntRange(from = 100, to = 699) int reasonHeaderCause,
@NonNull String reasonHeaderText) throws ImsException;
+
+ /**
+ * Provide the framework with a subsequent network response update to
+ * {@link #publishCapabilities(String, PublishResponseCallback)}.
+ *
+ * @param details The SIP information received in response to a publish operation.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework. This can happen if the {@link RcsFeature}
+ * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+ * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
+ * when the Telephony stack has crashed.
+ */
+ default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+ }
}
/**
@@ -262,7 +285,11 @@ public class RcsCapabilityExchangeImplBase {
* {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
* {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
* This may also happen in rare cases when the Telephony stack has crashed.
+ *
+ * @deprecated Replaced sip information with the newly added
+ * {@link #onNetworkResponse(SipDetails)}.
*/
+ @Deprecated
void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
@NonNull String reason) throws ImsException;
@@ -285,13 +312,35 @@ public class RcsCapabilityExchangeImplBase {
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
* the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
* rare cases when the Telephony stack has crashed.
+ *
+ * @deprecated Replaced sip information with the newly added
+ * {@link #onNetworkResponse(SipDetails)}.
*/
+ @Deprecated
void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
@NonNull String reasonPhrase,
@IntRange(from = 100, to = 699) int reasonHeaderCause,
@NonNull String reasonHeaderText) throws ImsException;
/**
+ * Notify the framework of the response to the SUBSCRIBE request from
+ * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}.
+ * <p>
+ * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
+ * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
+ * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
+ * subsequent NOTIFY responses to the subscription.
+ *
+ * @param details The SIP information related to this request.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework. This can happen if the
+ * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+ * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
+ * This may also happen in rare cases when the Telephony stack has crashed.
+ */
+ default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {};
+
+ /**
* Notify the framework of the latest XML PIDF documents included in the network response
* for the requested contacts' capabilities requested by the Framework using
* {@link RcsUceAdapter#requestCapabilities(List, Executor,
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index d7cf2826e446..60a74788b709 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -307,4 +307,15 @@ interface IImsCallSession {
* @param extensions the header extensions to be sent
*/
void sendRtpHeaderExtensions(in List<RtpHeaderExtension> extensions);
+
+ /*
+ * Deliver the bitrate for the indicated media type, direction and bitrate to the upper layer.
+ *
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond This value is the bitrate received from the NW through the Recommended
+ * bitrate MAC Control Element message and ImsStack converts this value from MAC bitrate
+ * to audio/video codec bitrate (defined in TS26.114).
+ */
+ void callSessionNotifyAnbr(int mediaType, int direction, int bitsPerSecond);
}
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index 8afd85633322..9395fbd6a85e 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -194,4 +194,17 @@ oneway interface IImsCallSessionListener {
* @param callQuality then updated call quality
*/
void callQualityChanged(in CallQuality callQuality);
+
+ /**
+ * Access Network Bitrate Recommendation Query (ANBRQ), see 3GPP TS 26.114.
+ * This API triggers radio to send ANBRQ message to the access network to query the desired
+ * bitrate.
+ *
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond This value is the bitrate requested by the other party UE through
+ * RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate
+ * (defined in TS36.321, range: 0 ~ 8000 kbit/s).
+ */
+ void callSessionSendAnbrQuery(int mediaType, int direction, int bitsPerSecond);
}
diff --git a/telephony/java/com/android/internal/telephony/ISipDialogStateCallback.aidl b/telephony/java/com/android/internal/telephony/ISipDialogStateCallback.aidl
new file mode 100644
index 000000000000..6847eb4d92a3
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ISipDialogStateCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.telephony;
+
+import android.telephony.ims.SipDialogState;
+
+/**
+ * Provides callback interface for SipDialogState when a state is changed.
+ *
+ */
+oneway interface ISipDialogStateCallback {
+ void onActiveSipDialogsChanged(in List<SipDialogState> dialogs);
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 33da4549820e..c7ed347b15bc 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1321,6 +1321,16 @@ interface ITelephony {
String getImeiForSlot(int slotIndex, String callingPackage, String callingFeatureId);
/**
+ * Returns the primary IMEI of the device
+ *
+ * @param callingPackage The package name of the caller
+ * @param callingFeatureId The feature Id of the calling package
+ * @throws UnsupportedOperationException if the radio doesn't support this feature.
+ * @throws SecurityException if the caller does not have the required permission/privileges
+ */
+ String getPrimaryImei(String callingPackage, String callingFeatureId);
+
+ /**
* Returns the Type Allocation Code from the IMEI for the given slot.
*
* @param slotIndex - Which slot to retrieve the Type Allocation Code from.
@@ -2670,4 +2680,11 @@ interface ITelephony {
*/
void setCellBroadcastIdRanges(int subId, in List<CellBroadcastIdRange> ranges,
IIntegerConsumer callback);
+
+ /**
+ * Returns whether the domain selection service is supported.
+ *
+ * @return {@code true} if the domain selection service is supported.
+ */
+ boolean isDomainSelectionSupported();
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index a1257e39f668..37e26f647a96 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -500,7 +500,7 @@ public interface RILConstants {
int RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY = 149;
int RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS = 150;
int RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD = 151;
-
+ int RIL_REQUEST_DEVICE_IMEI = 152;
/* The following requests are not defined in RIL.h */
int RIL_REQUEST_HAL_NON_RIL_BASE = 200;
int RIL_REQUEST_GET_SLOT_STATUS = 200;
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index 8aaf18af0664..4da530442c49 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -56,6 +56,8 @@ public class InputDeviceTest {
assertEquals(device.getSources(), outDevice.getSources());
assertEquals(device.getKeyboardType(), outDevice.getKeyboardType());
assertEquals(device.getCountryCode(), outDevice.getCountryCode());
+ assertEquals(device.getKeyboardLanguageTag(), outDevice.getKeyboardLanguageTag());
+ assertEquals(device.getKeyboardLayoutType(), outDevice.getKeyboardLayoutType());
assertEquals(device.getMotionRanges().size(), outDevice.getMotionRanges().size());
KeyCharacterMap keyCharacterMap = device.getKeyCharacterMap();
@@ -87,6 +89,8 @@ public class InputDeviceTest {
.setHasSensor(true)
.setHasBattery(true)
.setCountryCode(InputDeviceCountryCode.INTERNATIONAL)
+ .setKeyboardLanguageTag("en-US")
+ .setKeyboardLayoutType("qwerty")
.setSupportsUsi(true)
.build();
diff --git a/tests/SurfaceControlViewHostTest/OWNERS b/tests/SurfaceControlViewHostTest/OWNERS
new file mode 100644
index 000000000000..0862c05e0ee4
--- /dev/null
+++ b/tests/SurfaceControlViewHostTest/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 2031af2cf0c9..1930a1c8bbb2 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -63,6 +63,7 @@ class LockStateTrackingRule : TestRule {
inner class Listener : TrustListener {
override fun onTrustChanged(
enabled: Boolean,
+ newlyUnlocked: Boolean,
userId: Int,
flags: Int,
trustGrantedMessages: MutableList<String>
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index 1f6bb2150643..156961312323 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -32,7 +32,8 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe
private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
- private static VcnCellUnderlyingNetworkTemplate.Builder getTestNetworkTemplateBuilder() {
+ // Public for use in UnderlyingNetworkControllerTest
+ public static VcnCellUnderlyingNetworkTemplate.Builder getTestNetworkTemplateBuilder() {
return new VcnCellUnderlyingNetworkTemplate.Builder()
.setMetered(MATCH_FORBIDDEN)
.setMinUpstreamBandwidthKbps(
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 40408880a2c6..1883c85b5249 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -100,14 +100,20 @@ public class VcnGatewayConnectionConfigTest {
EXPOSED_CAPS);
}
- // Public for use in VcnGatewayConnectionTest
- public static VcnGatewayConnectionConfig buildTestConfig() {
+ // Public for use in UnderlyingNetworkControllerTest
+ public static VcnGatewayConnectionConfig buildTestConfig(
+ List<VcnUnderlyingNetworkTemplate> nwTemplates) {
final VcnGatewayConnectionConfig.Builder builder =
- newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
+ newBuilder().setVcnUnderlyingNetworkPriorities(nwTemplates);
return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
}
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfig() {
+ return buildTestConfig(UNDERLYING_NETWORK_TEMPLATES);
+ }
+
private static VcnGatewayConnectionConfig.Builder newBuilder() {
// Append a unique identifier to the name prefix to guarantee that all created
// VcnGatewayConnectionConfigs have a unique name (required by VcnConfig).
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 1c21a067bde8..aad7a5eb295c 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -59,6 +59,7 @@ import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -70,6 +71,7 @@ import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import com.android.server.vcn.util.MtuUtils;
@@ -90,6 +92,8 @@ import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
+ private static final int PARALLEL_SA_COUNT = 4;
+
private VcnIkeSession mIkeSession;
private VcnNetworkAgent mNetworkAgent;
private Network mVcnNetwork;
@@ -227,16 +231,29 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
private void verifyVcnTransformsApplied(
VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform)
throws Exception {
+ verifyVcnTransformsApplied(
+ vcnGatewayConnection,
+ expectForwardTransform,
+ Collections.singletonList(getChildSessionCallback()));
+ }
+
+ private void verifyVcnTransformsApplied(
+ VcnGatewayConnection vcnGatewayConnection,
+ boolean expectForwardTransform,
+ List<VcnChildSessionCallback> callbacks)
+ throws Exception {
for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
- getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+ for (VcnChildSessionCallback cb : callbacks) {
+ cb.onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+ }
mTestLooper.dispatchAll();
- verify(mIpSecSvc)
+ verify(mIpSecSvc, times(callbacks.size()))
.applyTunnelModeTransform(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
}
- verify(mIpSecSvc, expectForwardTransform ? times(1) : never())
+ verify(mIpSecSvc, expectForwardTransform ? times(callbacks.size()) : never())
.applyTunnelModeTransform(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(DIRECTION_FWD), anyInt(), any());
@@ -416,6 +433,89 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
}
+ private List<VcnChildSessionCallback> openChildAndVerifyParallelSasRequested()
+ throws Exception {
+ doReturn(PARALLEL_SA_COUNT)
+ .when(mDeps)
+ .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP));
+
+ // Verify scheduled but not canceled when entering ConnectedState
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ triggerChildOpened();
+ mTestLooper.dispatchAll();
+
+ // Verify new child sessions requested
+ final ArgumentCaptor<VcnChildSessionCallback> captor =
+ ArgumentCaptor.forClass(VcnChildSessionCallback.class);
+ verify(mIkeSession, times(PARALLEL_SA_COUNT - 1))
+ .openChildSession(any(TunnelModeChildSessionParams.class), captor.capture());
+
+ return captor.getAllValues();
+ }
+
+ private List<VcnChildSessionCallback> verifyChildOpenedRequestsAndAppliesParallelSas()
+ throws Exception {
+ List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested();
+
+ verifyVcnTransformsApplied(mGatewayConnection, false, callbacks);
+
+ // Mock IKE calling of onOpened()
+ for (VcnChildSessionCallback cb : callbacks) {
+ cb.onOpened(mock(VcnChildSessionConfiguration.class));
+ }
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ return callbacks;
+ }
+
+ @Test
+ public void testChildOpenedWithParallelSas() throws Exception {
+ verifyChildOpenedRequestsAndAppliesParallelSas();
+ }
+
+ @Test
+ public void testOpportunisticSa_ignoresPreOpenFailures() throws Exception {
+ List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested();
+
+ for (VcnChildSessionCallback cb : callbacks) {
+ cb.onClosed();
+ cb.onClosedExceptionally(mock(IkeException.class));
+ }
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
+ }
+
+ private void verifyPostOpenFailuresCloseSession(boolean shouldCloseWithException)
+ throws Exception {
+ List<VcnChildSessionCallback> callbacks = verifyChildOpenedRequestsAndAppliesParallelSas();
+
+ for (VcnChildSessionCallback cb : callbacks) {
+ if (shouldCloseWithException) {
+ cb.onClosed();
+ } else {
+ cb.onClosedExceptionally(mock(IkeException.class));
+ }
+ }
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+
+ @Test
+ public void testOpportunisticSa_handlesPostOpenFailures_onClosed() throws Exception {
+ verifyPostOpenFailuresCloseSession(false /* shouldCloseWithException */);
+ }
+
+ @Test
+ public void testOpportunisticSa_handlesPostOpenFailures_onClosedExceptionally()
+ throws Exception {
+ verifyPostOpenFailuresCloseSession(true /* shouldCloseWithException */);
+ }
+
@Test
public void testInternalAndDnsAddressesChanged() throws Exception {
final List<LinkAddress> startingInternalAddrs =
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index a4ee2de9f433..692c8a8f0898 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -143,9 +143,9 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
capBuilder.setLinkDownstreamBandwidthKbps(TEST_DOWNSTREAM_BANDWIDTH);
capBuilder.setAdministratorUids(new int[] {TEST_UID});
final Network underlyingNetwork = mock(Network.class, CALLS_REAL_METHODS);
- UnderlyingNetworkRecord record = new UnderlyingNetworkRecord(
- underlyingNetwork,
- capBuilder.build(), new LinkProperties(), false);
+ UnderlyingNetworkRecord record =
+ getTestNetworkRecord(
+ underlyingNetwork, capBuilder.build(), new LinkProperties(), false);
final NetworkCapabilities vcnCaps =
VcnGatewayConnection.buildNetworkCapabilities(
VcnGatewayConnectionConfigTest.buildTestConfig(),
@@ -211,7 +211,7 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
doReturn(TEST_DNS_ADDRESSES).when(childSessionConfig).getInternalDnsServers();
UnderlyingNetworkRecord record =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities.Builder().build(),
underlyingLp,
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 7bafd243799f..bb123ffe3073 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -109,9 +109,23 @@ public class VcnGatewayConnectionTestBase {
protected static final long ELAPSED_REAL_TIME = 123456789L;
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
+ protected static UnderlyingNetworkRecord getTestNetworkRecord(
+ Network network,
+ NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties,
+ boolean isBlocked) {
+ return new UnderlyingNetworkRecord(
+ network,
+ networkCapabilities,
+ linkProperties,
+ isBlocked,
+ false /* isSelected */,
+ 0 /* priorityClass */);
+ }
+
protected static final String TEST_TCP_BUFFER_SIZES_1 = "1,2,3,4";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities(),
new LinkProperties(),
@@ -124,7 +138,7 @@ public class VcnGatewayConnectionTestBase {
protected static final String TEST_TCP_BUFFER_SIZES_2 = "2,3,4,5";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities(),
new LinkProperties(),
@@ -209,6 +223,9 @@ public class VcnGatewayConnectionTestBase {
doReturn(mWakeLock)
.when(mDeps)
.newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any());
+ doReturn(1)
+ .when(mDeps)
+ .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP));
setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM);
setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM);
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index b0d68952c39d..629e988495cc 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -16,6 +16,7 @@
package com.android.server.vcn.routeselection;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS;
@@ -24,8 +25,8 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
-import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
-import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.calculatePriorityClass;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
@@ -48,6 +49,7 @@ import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -64,6 +66,8 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -102,6 +106,7 @@ public class NetworkPriorityClassifierTest {
private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setSubscriptionIds(Set.of(SUB_ID))
.setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
@@ -135,25 +140,35 @@ public class NetworkPriorityClassifierTest {
false /* isInTestMode */));
doNothing().when(mVcnContext).ensureRunningOnLooperThread();
- mWifiNetworkRecord =
- new UnderlyingNetworkRecord(
- mNetwork,
- WIFI_NETWORK_CAPABILITIES,
- LINK_PROPERTIES,
- false /* isBlocked */);
-
- mCellNetworkRecord =
- new UnderlyingNetworkRecord(
- mNetwork,
- CELL_NETWORK_CAPABILITIES,
- LINK_PROPERTIES,
- false /* isBlocked */);
-
setupSystemService(
mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID);
when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
+
+ mWifiNetworkRecord =
+ getTestNetworkRecord(
+ WIFI_NETWORK_CAPABILITIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
+ mCellNetworkRecord =
+ getTestNetworkRecord(
+ CELL_NETWORK_CAPABILITIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
+ }
+
+ private UnderlyingNetworkRecord getTestNetworkRecord(
+ NetworkCapabilities nc, List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
+ return new UnderlyingNetworkRecord(
+ mNetwork,
+ nc,
+ LINK_PROPERTIES,
+ false /* isBlocked */,
+ mVcnContext,
+ underlyingNetworkTemplates,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ null /* currentlySelected */,
+ null /* carrierConfig */);
}
@Test
@@ -490,37 +505,72 @@ public class NetworkPriorityClassifierTest {
mSubscriptionSnapshot));
}
- private void verifyCalculatePriorityClass(
- UnderlyingNetworkRecord networkRecord, int expectedIndex) {
- final int priorityIndex =
- calculatePriorityClass(
+ private void verifyMatchCellWithRequiredCapabilities(
+ VcnCellUnderlyingNetworkTemplate template, boolean expectMatch) {
+ assertEquals(
+ expectMatch,
+ checkMatchesCellPriorityRule(
mVcnContext,
- networkRecord,
- VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+ template,
+ mCellNetworkRecord,
SUB_GROUP,
- mSubscriptionSnapshot,
- null /* currentlySelected */,
- null /* carrierConfig */);
+ mSubscriptionSnapshot));
+ }
- assertEquals(expectedIndex, priorityIndex);
+ @Test
+ public void testMatchCell() {
+ final VcnCellUnderlyingNetworkTemplate template =
+ getCellNetworkPriorityBuilder().setInternet(MATCH_REQUIRED).build();
+ verifyMatchCellWithRequiredCapabilities(template, true /* expectMatch */);
}
@Test
- public void testCalculatePriorityClass() throws Exception {
- verifyCalculatePriorityClass(mCellNetworkRecord, 2);
+ public void testMatchCellFail_RequiredCapabilitiesMissing() {
+ final VcnCellUnderlyingNetworkTemplate template =
+ getCellNetworkPriorityBuilder().setCbs(MATCH_REQUIRED).build();
+ verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */);
}
@Test
- public void testCalculatePriorityClassFailToMatchAny() throws Exception {
- final NetworkCapabilities nc =
+ public void testMatchCellFail_ForbiddenCapabilitiesFound() {
+ final VcnCellUnderlyingNetworkTemplate template =
+ getCellNetworkPriorityBuilder().setDun(MATCH_FORBIDDEN).build();
+ verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */);
+ }
+
+ @Test
+ public void testCalculatePriorityClass() throws Exception {
+ assertEquals(2, mCellNetworkRecord.priorityClass);
+ }
+
+ private void checkCalculatePriorityClassFailToMatchAny(
+ boolean hasInternet, int expectedPriorityClass) throws Exception {
+ final List<VcnUnderlyingNetworkTemplate> templatesRequireDun =
+ Collections.singletonList(
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setDun(MATCH_REQUIRED)
+ .build());
+
+ final NetworkCapabilities.Builder ncBuilder =
new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSignalStrength(WIFI_RSSI_LOW)
- .setSsid(SSID)
- .build();
- final UnderlyingNetworkRecord wifiNetworkRecord =
- new UnderlyingNetworkRecord(mNetwork, nc, LINK_PROPERTIES, false /* isBlocked */);
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ if (hasInternet) {
+ ncBuilder.addCapability(NET_CAPABILITY_INTERNET);
+ }
+
+ final UnderlyingNetworkRecord nonDunNetworkRecord =
+ getTestNetworkRecord(ncBuilder.build(), templatesRequireDun);
+
+ assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass);
+ }
- verifyCalculatePriorityClass(wifiNetworkRecord, PRIORITY_ANY);
+ @Test
+ public void testCalculatePriorityClassFailToMatchAny_InternetNetwork() throws Exception {
+ checkCalculatePriorityClassFailToMatchAny(true /* hasInternet */, PRIORITY_FALLBACK);
+ }
+
+ @Test
+ public void testCalculatePriorityClassFailToMatchAny_NonInternetNetwork() throws Exception {
+ checkCalculatePriorityClassFailToMatchAny(false /* hasInternet */, PRIORITY_INVALID);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index fad9669911bb..2941fdea20bb 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -16,18 +16,29 @@
package com.android.server.vcn.routeselection;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_REQUIRED;
+
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -42,7 +53,10 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplateTest;
import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
@@ -64,7 +78,10 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -95,11 +112,39 @@ public class UnderlyingNetworkControllerTest {
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build();
+ private static final NetworkCapabilities DUN_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+
+ private static final NetworkCapabilities CBS_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+
private static final LinkProperties INITIAL_LINK_PROPERTIES =
getLinkPropertiesWithName("initial_iface");
private static final LinkProperties UPDATED_LINK_PROPERTIES =
getLinkPropertiesWithName("updated_iface");
+ private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_DUN =
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setInternet(MATCH_ANY)
+ .setDun(MATCH_REQUIRED)
+ .build();
+
+ private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_CBS =
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setInternet(MATCH_ANY)
+ .setCbs(MATCH_REQUIRED)
+ .build();
+
@Mock private Context mContext;
@Mock private VcnNetworkProvider mVcnNetworkProvider;
@Mock private ConnectivityManager mConnectivityManager;
@@ -201,6 +246,107 @@ public class UnderlyingNetworkControllerTest {
any());
}
+ private void verifyRequestBackgroundNetwork(
+ ConnectivityManager cm,
+ int expectedSubId,
+ Set<Integer> expectedRequiredCaps,
+ Set<Integer> expectedForbiddenCaps) {
+ verify(cm)
+ .requestBackgroundNetwork(
+ eq(
+ getCellRequestForSubId(
+ expectedSubId,
+ expectedRequiredCaps,
+ expectedForbiddenCaps)),
+ any(NetworkBringupCallback.class),
+ any());
+ }
+
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartupForNonInternetCapabilities() {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
+ // Build network templates
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+
+ networkTemplates.add(
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder()
+ .setDun(MATCH_REQUIRED)
+ .setInternet(MATCH_ANY)
+ .build());
+
+ networkTemplates.add(
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder()
+ .setMms(MATCH_REQUIRED)
+ .setCbs(MATCH_FORBIDDEN)
+ .setInternet(MATCH_ANY)
+ .build());
+
+ // Start UnderlyingNetworkController
+ new UnderlyingNetworkController(
+ mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mNetworkControllerCb);
+
+ // Verifications
+ for (final int subId : INITIAL_SUB_IDS) {
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_INTERNET),
+ Collections.emptySet());
+ verifyRequestBackgroundNetwork(
+ cm, subId, Collections.singleton(NET_CAPABILITY_DUN), Collections.emptySet());
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_MMS),
+ Collections.singleton(NET_CAPABILITY_CBS));
+ }
+ }
+
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartupWithDedupedtCapabilities() {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
+ // Build network templates
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+ final VcnCellUnderlyingNetworkTemplate.Builder builder =
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMms(MATCH_REQUIRED)
+ .setCbs(MATCH_FORBIDDEN)
+ .setInternet(MATCH_ANY);
+
+ networkTemplates.add(builder.setMetered(MATCH_REQUIRED).build());
+ networkTemplates.add(builder.setMetered(MATCH_FORBIDDEN).build());
+
+ // Start UnderlyingNetworkController
+ new UnderlyingNetworkController(
+ mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mNetworkControllerCb);
+
+ // Verifications
+ for (final int subId : INITIAL_SUB_IDS) {
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_INTERNET),
+ Collections.emptySet());
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_MMS),
+ Collections.singleton(NET_CAPABILITY_CBS));
+ }
+ }
+
private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
@@ -210,8 +356,13 @@ public class UnderlyingNetworkControllerTest {
for (final int subId : expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
- eq(getCellRequestForSubId(subId)),
- any(NetworkBringupCallback.class), any());
+ eq(
+ getCellRequestForSubId(
+ subId,
+ Collections.singleton(NET_CAPABILITY_INTERNET),
+ Collections.emptySet())),
+ any(NetworkBringupCallback.class),
+ any());
}
verify(mConnectivityManager)
@@ -253,6 +404,7 @@ public class UnderlyingNetworkControllerTest {
private NetworkRequest getWifiRequest(Set<Integer> netCapsSubIds) {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.build();
}
@@ -261,6 +413,7 @@ public class UnderlyingNetworkControllerTest {
// TODO (b/187991063): Add tests for carrier-config based thresholds
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.setSignalStrength(WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
.build();
@@ -270,16 +423,27 @@ public class UnderlyingNetworkControllerTest {
// TODO (b/187991063): Add tests for carrier-config based thresholds
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.setSignalStrength(WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
.build();
}
- private NetworkRequest getCellRequestForSubId(int subId) {
- return getExpectedRequestBase()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
- .build();
+ private NetworkRequest getCellRequestForSubId(
+ int subId, Set<Integer> requiredCaps, Set<Integer> forbiddenCaps) {
+ final NetworkRequest.Builder nqBuilder =
+ getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+
+ for (int cap : requiredCaps) {
+ nqBuilder.addCapability(cap);
+ }
+ for (int cap : forbiddenCaps) {
+ nqBuilder.addForbiddenCapability(cap);
+ }
+
+ return nqBuilder.build();
}
private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
@@ -301,7 +465,6 @@ public class UnderlyingNetworkControllerTest {
private NetworkRequest.Builder getExpectedRequestBase() {
final NetworkRequest.Builder builder =
new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
@@ -321,16 +484,30 @@ public class UnderlyingNetworkControllerTest {
.unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
}
+ private static UnderlyingNetworkRecord getTestNetworkRecord(
+ Network network,
+ NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties,
+ boolean isBlocked) {
+ return new UnderlyingNetworkRecord(
+ network,
+ networkCapabilities,
+ linkProperties,
+ isBlocked,
+ false /* isSelected */,
+ 0 /* priorityClass */);
+ }
+
@Test
public void testUnderlyingNetworkRecordEquals() {
UnderlyingNetworkRecord recordA =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
UnderlyingNetworkRecord recordB =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
@@ -338,12 +515,24 @@ public class UnderlyingNetworkControllerTest {
UnderlyingNetworkRecord recordC =
new UnderlyingNetworkRecord(
mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */,
+ true /* isSelected */,
+ -1 /* priorityClass */);
+ UnderlyingNetworkRecord recordD =
+ getTestNetworkRecord(
+ mNetwork,
UPDATED_NETWORK_CAPABILITIES,
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
assertEquals(recordA, recordB);
- assertNotEquals(recordA, recordC);
+ assertEquals(recordA, recordC);
+ assertNotEquals(recordA, recordD);
+
+ assertTrue(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordB));
+ assertFalse(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordC));
}
@Test
@@ -366,6 +555,10 @@ public class UnderlyingNetworkControllerTest {
.build();
}
+ private void verifyOnSelectedUnderlyingNetworkChanged(UnderlyingNetworkRecord expectedRecord) {
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback(
NetworkCapabilities networkCapabilities) {
verify(mConnectivityManager)
@@ -384,12 +577,12 @@ public class UnderlyingNetworkControllerTest {
cb.onBlockedStatusChanged(mNetwork, false /* isFalse */);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
return cb;
}
@@ -402,12 +595,12 @@ public class UnderlyingNetworkControllerTest {
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -417,12 +610,12 @@ public class UnderlyingNetworkControllerTest {
cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS),
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -434,18 +627,16 @@ public class UnderlyingNetworkControllerTest {
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -458,18 +649,16 @@ public class UnderlyingNetworkControllerTest {
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -478,13 +667,7 @@ public class UnderlyingNetworkControllerTest {
cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
- UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
- mNetwork,
- buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS),
- INITIAL_LINK_PROPERTIES,
- true /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(null);
}
@Test
@@ -520,5 +703,132 @@ public class UnderlyingNetworkControllerTest {
verify(mNetworkControllerCb, times(1)).onSelectedUnderlyingNetworkChanged(any());
}
- // TODO (b/187991063): Add tests for network prioritization
+ private UnderlyingNetworkListener setupControllerAndGetNetworkListener(
+ List<VcnUnderlyingNetworkTemplate> networkTemplates) {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
+ new UnderlyingNetworkController(
+ mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mNetworkControllerCb);
+
+ verify(cm)
+ .registerNetworkCallback(
+ eq(getRouteSelectionRequest(INITIAL_SUB_IDS)),
+ mUnderlyingNetworkListenerCaptor.capture(),
+ any());
+
+ return mUnderlyingNetworkListenerCaptor.getValue();
+ }
+
+ private UnderlyingNetworkRecord bringupNetworkAndGetRecord(
+ UnderlyingNetworkListener cb,
+ NetworkCapabilities requestNetworkCaps,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ UnderlyingNetworkRecord currentlySelected) {
+ final Network network = mock(Network.class);
+ final NetworkCapabilities responseNetworkCaps =
+ buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS);
+
+ cb.onAvailable(network);
+ cb.onCapabilitiesChanged(network, responseNetworkCaps);
+ cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES);
+ cb.onBlockedStatusChanged(network, false /* isFalse */);
+ return new UnderlyingNetworkRecord(
+ network,
+ responseNetworkCaps,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */,
+ mVcnContext,
+ underlyingNetworkTemplates,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ currentlySelected,
+ null /* carrierConfig */);
+ }
+
+ @Test
+ public void testSelectMorePreferredNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+ networkTemplates.add(CELL_TEMPLATE_DUN);
+ networkTemplates.add(CELL_TEMPLATE_CBS);
+
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ // Bring up CBS network
+ final UnderlyingNetworkRecord cbsNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb,
+ CBS_NETWORK_CAPABILITIES,
+ networkTemplates,
+ null /* currentlySelected */);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+
+ // Bring up DUN network
+ final UnderlyingNetworkRecord dunNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+ }
+
+ @Test
+ public void testNeverSelectLessPreferredNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+ networkTemplates.add(CELL_TEMPLATE_DUN);
+ networkTemplates.add(CELL_TEMPLATE_CBS);
+
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ // Bring up DUN network
+ final UnderlyingNetworkRecord dunNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb,
+ DUN_NETWORK_CAPABILITIES,
+ networkTemplates,
+ null /* currentlySelected */);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+
+ // Bring up CBS network
+ final UnderlyingNetworkRecord cbsNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord);
+ verify(mNetworkControllerCb, never())
+ .onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+ }
+
+ @Test
+ public void testFailtoMatchTemplateAndFallBackToInternetNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+
+ networkTemplates.add(
+ new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ // Bring up an Internet network without DUN capability
+ final UnderlyingNetworkRecord networkRecord =
+ bringupNetworkAndGetRecord(
+ cb,
+ INITIAL_NETWORK_CAPABILITIES,
+ networkTemplates,
+ null /* currentlySelected */);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord));
+ }
+
+ @Test
+ public void testFailtoMatchTemplateAndNeverFallBackToNonInternetNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+
+ networkTemplates.add(
+ new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ bringupNetworkAndGetRecord(
+ cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */);
+
+ verify(mNetworkControllerCb, never())
+ .onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class));
+ }
}
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 5da18dc8ccad..28576bf159ee 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -234,7 +234,11 @@ public class WifiNl80211Manager {
/**
* Result of a signal poll requested using {@link #signalPoll(String)}.
+ *
+ * @deprecated The usage is replaced by
+ * {@code com.android.server.wifi.WifiSignalPollResults}.
*/
+ @Deprecated
public static class SignalPollResult {
/** @hide */
public SignalPollResult(int currentRssiDbm, int txBitrateMbps, int rxBitrateMbps,
@@ -876,7 +880,11 @@ public class WifiNl80211Manager {
*
* @return A {@link SignalPollResult} object containing interface statistics, or a null on
* error (e.g. the interface hasn't been set up yet).
+ *
+ * @deprecated replaced by
+ * {@link com.android.server.wifi.SupplicantStaIfaceHal#getSignalPollResults}
*/
+ @Deprecated
@Nullable public SignalPollResult signalPoll(@NonNull String ifaceName) {
IClientInterface iface = getClientInterface(ifaceName);
if (iface == null) {