diff options
952 files changed, 18562 insertions, 7176 deletions
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb index 80317e4634f3..78c48200cfc5 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7396576" + build_id: "7471822" target: "CtsShim" source_file: "aosp_arm64/CtsShimPriv.apk" } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb index 3605b6d0433b..a9632ad05ac0 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7396576" + build_id: "7471822" target: "CtsShim" source_file: "aosp_arm64/CtsShim.apk" } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb index 025ec3a9fdaf..df3a0bbe9f2c 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7396576" + build_id: "7471822" target: "CtsShim" source_file: "aosp_x86_64/CtsShimPriv.apk" } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb index e19235a12f8f..1bc6cb8706f2 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7396576" + build_id: "7471822" target: "CtsShim" source_file: "aosp_x86_64/CtsShim.apk" } diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp index 9a420160d3fd..827842633942 100644 --- a/apex/appsearch/Android.bp +++ b/apex/appsearch/Android.bp @@ -29,6 +29,7 @@ apex { key: "com.android.appsearch.key", certificate: ":com.android.appsearch.certificate", updatable: false, + generate_hashtree: false, } apex_key { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index c7c1d68b90f1..400c24138060 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -37,6 +37,7 @@ import android.app.appsearch.aidl.AppSearchResultParcel; import android.app.appsearch.aidl.IAppSearchBatchResultCallback; import android.app.appsearch.aidl.IAppSearchManager; import android.app.appsearch.aidl.IAppSearchResultCallback; +import android.app.appsearch.exceptions.AppSearchException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -58,11 +59,9 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalManagerRegistry; import com.android.server.SystemService; -import com.android.server.appsearch.external.localstorage.AppSearchImpl; import com.android.server.appsearch.external.localstorage.stats.CallStats; -import com.android.server.appsearch.stats.LoggerInstanceManager; -import com.android.server.appsearch.stats.PlatformLogger; import com.android.server.appsearch.util.PackageUtil; +import com.android.server.appsearch.visibilitystore.VisibilityStore; import com.android.server.usage.StorageStatsManagerLocal; import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter; @@ -88,9 +87,8 @@ public class AppSearchManagerService extends SystemService { private static final String TAG = "AppSearchManagerService"; private final Context mContext; private PackageManager mPackageManager; - private ImplInstanceManager mImplInstanceManager; private UserManager mUserManager; - private LoggerInstanceManager mLoggerInstanceManager; + private AppSearchUserInstanceManager mAppSearchUserInstanceManager; // Never call shutdownNow(). It will cancel the futures it's returned. And since // Executor#execute won't return anything, we will hang forever waiting for the execution. @@ -115,9 +113,8 @@ public class AppSearchManagerService extends SystemService { public void onStart() { publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); mPackageManager = getContext().getPackageManager(); - mImplInstanceManager = ImplInstanceManager.getInstance(mContext); + mAppSearchUserInstanceManager = AppSearchUserInstanceManager.getInstance(); mUserManager = mContext.getSystemService(UserManager.class); - mLoggerInstanceManager = LoggerInstanceManager.getInstance(); registerReceivers(); LocalManagerRegistry.getManager(StorageStatsManagerLocal.class) .registerStorageStatsAugmenter(new AppSearchStorageStatsAugmenter(), TAG); @@ -179,8 +176,7 @@ public class AppSearchManagerService extends SystemService { */ private void handleUserRemoved(@NonNull UserHandle userHandle) { try { - mImplInstanceManager.closeAndRemoveAppSearchImplForUser(userHandle); - mLoggerInstanceManager.removePlatformLoggerForUser(userHandle); + mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle); Log.i(TAG, "Removed AppSearchImpl instance for: " + userHandle); } catch (Throwable t) { Log.e(TAG, "Unable to remove data for: " + userHandle, t); @@ -218,18 +214,18 @@ public class AppSearchManagerService extends SystemService { UserHandle userHandle = UserHandle.getUserHandleForUid(uid); try { if (isUserLocked(userHandle)) { - //TODO(b/186151459) clear the uninstalled package data when user is unlocked. + // We cannot access a locked user's directry and remove package data from it. + // We should remove those uninstalled package data when the user is unlocking. return; } - if (ImplInstanceManager.getAppSearchDir(userHandle).exists()) { - // Only clear the package's data if AppSearch exists for this user. - PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(mContext, - userHandle, AppSearchConfig.getInstance(EXECUTOR)); - AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, - userHandle, logger); + // Only clear the package's data if AppSearch exists for this user. + if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) { + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); //TODO(b/145759910) clear visibility setting for package. - impl.clearPackageData(packageName); - logger.removeCachedUidForPackage(packageName); + instance.getAppSearchImpl().clearPackageData(packageName); + instance.getLogger().removeCachedUidForPackage(packageName); } } catch (Throwable t) { Log.e(TAG, "Unable to remove data for package: " + packageName, t); @@ -239,9 +235,33 @@ public class AppSearchManagerService extends SystemService { @Override public void onUserUnlocking(@NonNull TargetUser user) { Objects.requireNonNull(user); + UserHandle userHandle = user.getUserHandle(); synchronized (mUnlockedUsersLocked) { - mUnlockedUsersLocked.add(user.getUserHandle()); + mUnlockedUsersLocked.add(userHandle); } + EXECUTOR.execute(() -> { + try { + // Only clear the package's data if AppSearch exists for this user. + if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) { + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); + List<PackageInfo> installedPackageInfos = mContext + .createContextAsUser(userHandle, /*flags=*/0) + .getPackageManager() + .getInstalledPackages(/*flags=*/0); + Set<String> packagesToKeep = new ArraySet<>(installedPackageInfos.size()); + for (int i = 0; i < installedPackageInfos.size(); i++) { + packagesToKeep.add(installedPackageInfos.get(i).packageName); + } + packagesToKeep.add(VisibilityStore.PACKAGE_NAME); + //TODO(b/145759910) clear visibility setting for package. + instance.getAppSearchImpl().prunePackageData(packagesToKeep); + } + } catch (Throwable t) { + Log.e(TAG, "Unable to prune packages for " + user, t); + } + }); } @Override @@ -252,7 +272,7 @@ public class AppSearchManagerService extends SystemService { UserHandle userHandle = user.getUserHandle(); mUnlockedUsersLocked.remove(userHandle); try { - mImplInstanceManager.closeAndRemoveAppSearchImplForUser(userHandle); + mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle); } catch (Throwable t) { Log.e(TAG, "Error handling user stopping.", t); } @@ -284,7 +304,7 @@ public class AppSearchManagerService extends SystemService { @NonNull String databaseName, @NonNull List<Bundle> schemaBundles, @NonNull List<String> schemasNotDisplayedBySystem, - @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles, + @NonNull Map<String, List<Bundle>> schemasVisibleToPackagesBundles, boolean forceOverride, int schemaVersion, @NonNull UserHandle userHandle, @@ -294,7 +314,7 @@ public class AppSearchManagerService extends SystemService { Objects.requireNonNull(databaseName); Objects.requireNonNull(schemaBundles); Objects.requireNonNull(schemasNotDisplayedBySystem); - Objects.requireNonNull(schemasPackageAccessibleBundles); + Objects.requireNonNull(schemasVisibleToPackagesBundles); Objects.requireNonNull(userHandle); Objects.requireNonNull(callback); @@ -303,7 +323,7 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { @@ -314,9 +334,9 @@ public class AppSearchManagerService extends SystemService { schemas.add(new AppSearchSchema(schemaBundles.get(i))); } Map<String, List<PackageIdentifier>> schemasPackageAccessible = - new ArrayMap<>(schemasPackageAccessibleBundles.size()); + new ArrayMap<>(schemasVisibleToPackagesBundles.size()); for (Map.Entry<String, List<Bundle>> entry : - schemasPackageAccessibleBundles.entrySet()) { + schemasVisibleToPackagesBundles.entrySet()) { List<PackageIdentifier> packageIdentifiers = new ArrayList<>(entry.getValue().size()); for (int i = 0; i < entry.getValue().size(); i++) { @@ -325,12 +345,12 @@ public class AppSearchManagerService extends SystemService { } schemasPackageAccessible.put(entry.getKey(), packageIdentifiers); } - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - SetSchemaResponse setSchemaResponse = impl.setSchema( + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema( packageName, databaseName, schemas, + instance.getVisibilityStore(), schemasNotDisplayedBySystem, schemasPackageAccessible, forceOverride, @@ -343,24 +363,24 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - CallStats.Builder cBuilder = new CallStats.Builder(packageName, - databaseName) + instance.getLogger().logStats(new CallStats.Builder() + .setPackageName(packageName) + .setDatabase(databaseName) + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_SET_SCHEMA) // TODO(b/173532925) check the existing binder call latency chart // is good enough for us: // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount); - cBuilder.getGeneralStatsBuilder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis); - logger.logStats(cBuilder.build()); + .setNumOperationsFailed(operationFailureCount) + .build()); } } }); @@ -383,8 +403,10 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - GetSchemaResponse response = impl.getSchema(packageName, databaseName); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + GetSchemaResponse response = + instance.getAppSearchImpl().getSchema(packageName, databaseName); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(response.getBundle())); @@ -411,10 +433,12 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - List<String> namespaces = impl.getNamespaces(packageName, databaseName); - invokeCallbackOnResult(callback, - AppSearchResult.newSuccessfulResult(namespaces)); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + List<String> namespaces = + instance.getAppSearchImpl().getNamespaces(packageName, databaseName); + invokeCallbackOnResult( + callback, AppSearchResult.newSuccessfulResult(namespaces)); } catch (Throwable t) { invokeCallbackOnError(callback, t); } @@ -440,7 +464,7 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { @@ -448,17 +472,16 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUser, callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); for (int i = 0; i < documentBundles.size(); i++) { GenericDocument document = new GenericDocument(documentBundles.get(i)); try { - impl.putDocument(packageName, databaseName, document, logger); - resultBuilder.setSuccess(document.getId(), /*result=*/ null); + instance.getAppSearchImpl().putDocument( + packageName, databaseName, document, instance.getLogger()); + resultBuilder.setSuccess(document.getId(), /*value=*/ null); ++operationSuccessCount; } catch (Throwable t) { - resultBuilder.setResult(document.getId(), - throwableToFailedResult(t)); + resultBuilder.setResult(document.getId(), throwableToFailedResult(t)); AppSearchResult<Void> result = throwableToFailedResult(t); resultBuilder.setResult(document.getId(), result); // Since we can only include one status code in the atom, @@ -468,31 +491,31 @@ public class AppSearchManagerService extends SystemService { } } // Now that the batch has been written. Persist the newly written data. - impl.persistToDisk(PersistType.Code.LITE); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { ++operationFailureCount; statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - CallStats.Builder cBuilder = new CallStats.Builder(packageName, - databaseName) + instance.getLogger().logStats(new CallStats.Builder() + .setPackageName(packageName) + .setDatabase(databaseName) + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_PUT_DOCUMENTS) // TODO(b/173532925) check the existing binder call latency chart // is good enough for us: // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount); - cBuilder.getGeneralStatsBuilder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis); - logger.logStats(cBuilder.build()); + .setNumOperationsFailed(operationFailureCount) + .build()); } } }); @@ -521,7 +544,7 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { @@ -529,18 +552,16 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUser, callingUid, packageName); AppSearchBatchResult.Builder<String, Bundle> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); for (int i = 0; i < ids.size(); i++) { String id = ids.get(i); try { - GenericDocument document = - impl.getDocument( - packageName, - databaseName, - namespace, - id, - typePropertyPaths); + GenericDocument document = instance.getAppSearchImpl().getDocument( + packageName, + databaseName, + namespace, + id, + typePropertyPaths); ++operationSuccessCount; resultBuilder.setSuccess(id, document.getBundle()); } catch (Throwable t) { @@ -558,24 +579,24 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - CallStats.Builder cBuilder = new CallStats.Builder(packageName, - databaseName) + instance.getLogger().logStats(new CallStats.Builder() + .setPackageName(packageName) + .setDatabase(databaseName) + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_GET_DOCUMENTS) // TODO(b/173532925) check the existing binder call latency chart // is good enough for us: // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount); - cBuilder.getGeneralStatsBuilder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis); - logger.logStats(cBuilder.build()); + .setNumOperationsFailed(operationFailureCount) + .build()); } } }); @@ -602,21 +623,19 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - SearchResultPage searchResultPage = - impl.query( - packageName, - databaseName, - queryExpression, - new SearchSpec(searchSpecBundle), - logger); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + SearchResultPage searchResultPage = instance.getAppSearchImpl().query( + packageName, + databaseName, + queryExpression, + new SearchSpec(searchSpecBundle), + instance.getLogger()); ++operationSuccessCount; invokeCallbackOnResult( callback, @@ -626,24 +645,24 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - CallStats.Builder cBuilder = new CallStats.Builder(packageName, - databaseName) + instance.getLogger().logStats(new CallStats.Builder() + .setPackageName(packageName) + .setDatabase(databaseName) + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_SEARCH) // TODO(b/173532925) check the existing binder call latency chart // is good enough for us: // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount); - cBuilder.getGeneralStatsBuilder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis); - logger.logStats(cBuilder.build()); + .setNumOperationsFailed(operationFailureCount) + .build()); } } }); @@ -668,21 +687,24 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - SearchResultPage searchResultPage = - impl.globalQuery( - queryExpression, - new SearchSpec(searchSpecBundle), - packageName, - callingUid, - logger); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + + boolean callerHasSystemAccess = + instance.getVisibilityStore().doesCallerHaveSystemAccess(packageName); + SearchResultPage searchResultPage = instance.getAppSearchImpl().globalQuery( + queryExpression, + new SearchSpec(searchSpecBundle), + packageName, + instance.getVisibilityStore(), + callingUid, + callerHasSystemAccess, + instance.getLogger()); ++operationSuccessCount; invokeCallbackOnResult( callback, @@ -692,25 +714,23 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - // TODO(b/173532925) database would be nulluable once we remove generalStats - CallStats.Builder cBuilder = new CallStats.Builder(packageName, - /*database=*/ "") + instance.getLogger().logStats(new CallStats.Builder() + .setPackageName(packageName) + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_GLOBAL_SEARCH) // TODO(b/173532925) check the existing binder call latency chart // is good enough for us: // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount); - cBuilder.getGeneralStatsBuilder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis); - logger.logStats(cBuilder.build()); + .setNumOperationsFailed(operationFailureCount) + .build()); } } }); @@ -731,8 +751,10 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyUserUnlocked(callingUser); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - SearchResultPage searchResultPage = impl.getNextPage(nextPageToken); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + SearchResultPage searchResultPage = + instance.getAppSearchImpl().getNextPage(nextPageToken); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); @@ -751,8 +773,9 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyUserUnlocked(callingUser); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - impl.invalidateNextPageToken(nextPageToken); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + instance.getAppSearchImpl().invalidateNextPageToken(nextPageToken); } catch (Throwable t) { Log.e(TAG, "Unable to invalidate the query page token", t); } @@ -781,11 +804,12 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); // we don't need to append the file. The file is always brand new. try (DataOutputStream outputStream = new DataOutputStream( new FileOutputStream(fileDescriptor.getFileDescriptor()))) { - SearchResultPage searchResultPage = impl.query( + SearchResultPage searchResultPage = instance.getAppSearchImpl().query( packageName, databaseName, queryExpression, @@ -797,7 +821,7 @@ public class AppSearchManagerService extends SystemService { outputStream, searchResultPage.getResults().get(i) .getGenericDocument().getBundle()); } - searchResultPage = impl.getNextPage( + searchResultPage = instance.getAppSearchImpl().getNextPage( searchResultPage.getNextPageToken()); } } @@ -826,7 +850,8 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); GenericDocument document; ArrayList<Bundle> migrationFailureBundles = new ArrayList<>(); @@ -841,8 +866,8 @@ public class AppSearchManagerService extends SystemService { break; } try { - impl.putDocument(packageName, databaseName, document, - /*logger=*/ null); + instance.getAppSearchImpl().putDocument( + packageName, databaseName, document, /*logger=*/ null); } catch (Throwable t) { migrationFailureBundles.add(new SetSchemaResponse.MigrationFailure( document.getNamespace(), @@ -853,7 +878,7 @@ public class AppSearchManagerService extends SystemService { } } } - impl.persistToDisk(PersistType.Code.FULL); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(migrationFailureBundles)); } catch (Throwable t) { @@ -884,17 +909,23 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyUserUnlocked(callingUser); - - if (systemUsage) { - // TODO(b/183031844): Validate that the call comes from the system + verifyCallingPackage(callingUser, callingUid, packageName); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + + if (systemUsage + && !instance.getVisibilityStore() + .doesCallerHaveSystemAccess(packageName)) { + throw new AppSearchException( + AppSearchResult.RESULT_SECURITY_ERROR, + packageName + " does not have access to report system usage"); } - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - impl.reportUsage( + instance.getAppSearchImpl().reportUsage( packageName, databaseName, namespace, documentId, usageTimeMillis, systemUsage); invokeCallbackOnResult( - callback, AppSearchResult.newSuccessfulResult(/*result=*/ null)); + callback, AppSearchResult.newSuccessfulResult(/*value=*/ null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); } @@ -922,7 +953,7 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { @@ -930,12 +961,11 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUser, callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); for (int i = 0; i < ids.size(); i++) { String id = ids.get(i); try { - impl.remove( + instance.getAppSearchImpl().remove( packageName, databaseName, namespace, @@ -953,31 +983,31 @@ public class AppSearchManagerService extends SystemService { } } // Now that the batch has been written. Persist the newly written data. - impl.persistToDisk(PersistType.Code.LITE); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { ++operationFailureCount; statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - CallStats.Builder cBuilder = new CallStats.Builder(packageName, - databaseName) + instance.getLogger().logStats(new CallStats.Builder() + .setPackageName(packageName) + .setDatabase(databaseName) + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID) // TODO(b/173532925) check the existing binder call latency chart // is good enough for us: // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount); - cBuilder.getGeneralStatsBuilder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis); - logger.logStats(cBuilder.build()); + .setNumOperationsFailed(operationFailureCount) + .build()); } } }); @@ -1005,22 +1035,21 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - impl.removeByQuery( + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + instance.getAppSearchImpl().removeByQuery( packageName, databaseName, queryExpression, new SearchSpec(searchSpecBundle), /*removeStatsBuilder=*/ null); // Now that the batch has been written. Persist the newly written data. - impl.persistToDisk(PersistType.Code.LITE); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); ++operationSuccessCount; invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { @@ -1028,24 +1057,24 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - CallStats.Builder cBuilder = new CallStats.Builder(packageName, - databaseName) + instance.getLogger().logStats(new CallStats.Builder() + .setPackageName(packageName) + .setDatabase(databaseName) + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH) // TODO(b/173532925) check the existing binder call latency chart // is good enough for us: // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount); - cBuilder.getGeneralStatsBuilder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis); - logger.logStats(cBuilder.build()); + .setNumOperationsFailed(operationFailureCount) + .build()); } } }); @@ -1068,9 +1097,10 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName, - databaseName); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + StorageInfo storageInfo = instance.getAppSearchImpl() + .getStorageInfoForDatabase(packageName, databaseName); Bundle storageInfoBundle = storageInfo.getBundle(); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(storageInfoBundle)); @@ -1091,38 +1121,35 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - impl.persistToDisk(PersistType.Code.FULL); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); ++operationSuccessCount; } catch (Throwable t) { ++operationFailureCount; statusCode = throwableToFailedResult(t).getResultCode(); Log.e(TAG, "Unable to persist the data to disk", t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - CallStats.Builder cBuilder = new CallStats.Builder(/*packageName=*/ "", - /*databaseName=*/ "") + instance.getLogger().logStats(new CallStats.Builder() + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_FLUSH) // TODO(b/173532925) check the existing binder call latency chart // is good enough for us: // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount); - cBuilder.getGeneralStatsBuilder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis); - logger.logStats(cBuilder.build()); + .setNumOperationsFailed(operationFailureCount) + .build()); } } }); @@ -1141,15 +1168,13 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); - logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, callingUser, - AppSearchConfig.getInstance(EXECUTOR)); - mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUser, logger); + instance = mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, callingUser, AppSearchConfig.getInstance(EXECUTOR)); ++operationSuccessCount; invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { @@ -1157,26 +1182,22 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - // TODO(b/173532925) make packageName and database nullable after - // removing generalStats - CallStats.Builder cBuilder = new CallStats.Builder(/*packageName=*/"", - /*database=*/ "") + instance.getLogger().logStats(new CallStats.Builder() + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_INITIALIZE) // TODO(b/173532925) check the existing binder call latency chart // is good enough for us: // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount); - cBuilder.getGeneralStatsBuilder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis); - logger.logStats(cBuilder.build()); + .setNumOperationsFailed(operationFailureCount) + .build()); } } }); @@ -1263,9 +1284,6 @@ public class AppSearchManagerService extends SystemService { * @param callingUid The actual uid of the caller as determined by Binder. * @return the user handle that the call should run as. Will always be a concrete user. */ - // TODO(b/173553485) verifying that the caller has permission to access target user's data - // TODO(b/173553485) Handle ACTION_USER_REMOVED broadcast - // TODO(b/173553485) Implement SystemService.onUserStopping() @NonNull private UserHandle handleIncomingUser(@NonNull UserHandle requestedUser, int callingUid) { int callingPid = Binder.getCallingPid(); @@ -1313,12 +1331,11 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(userHandle); - PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle, - AppSearchConfig.getInstance(EXECUTOR)); - AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl( - mContext, userHandle, logger); - stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes(); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); + stats.dataSize += instance.getAppSearchImpl() + .getStorageInfoForPackage(packageName).getSizeBytes(); } catch (Throwable t) { Log.e( TAG, @@ -1342,14 +1359,12 @@ public class AppSearchManagerService extends SystemService { if (packagesForUid == null) { return; } - PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle, - AppSearchConfig.getInstance(EXECUTOR)); - AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl( - mContext, userHandle, logger); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); for (int i = 0; i < packagesForUid.length; i++) { - stats.dataSize += - impl.getStorageInfoForPackage(packagesForUid[i]).getSizeBytes(); + stats.dataSize += instance.getAppSearchImpl() + .getStorageInfoForPackage(packagesForUid[i]).getSizeBytes(); } } catch (Throwable t) { Log.e(TAG, "Unable to augment storage stats for uid " + uid, t); @@ -1372,14 +1387,13 @@ public class AppSearchManagerService extends SystemService { if (packagesForUser == null) { return; } - PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle, - AppSearchConfig.getInstance(EXECUTOR)); - AppSearchImpl impl = - mImplInstanceManager.getOrCreateAppSearchImpl(mContext, userHandle, logger); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); for (int i = 0; i < packagesForUser.size(); i++) { String packageName = packagesForUser.get(i).packageName; - stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes(); + stats.dataSize += instance.getAppSearchImpl() + .getStorageInfoForPackage(packageName).getSizeBytes(); } } catch (Throwable t) { Log.e(TAG, "Unable to augment storage stats for " + userHandle, t); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java new file mode 100644 index 000000000000..7e743edaf3ee --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch; + +import android.annotation.NonNull; + +import com.android.server.appsearch.external.localstorage.AppSearchImpl; +import com.android.server.appsearch.stats.PlatformLogger; +import com.android.server.appsearch.visibilitystore.VisibilityStore; + +import java.util.Objects; + +/** + * Container for AppSearch classes that should only be initialized once per device-user and make up + * the core of the AppSearch system. + */ +public final class AppSearchUserInstance { + private final PlatformLogger mLogger; + private final AppSearchImpl mAppSearchImpl; + private final VisibilityStore mVisibilityStore; + + AppSearchUserInstance( + @NonNull PlatformLogger logger, + @NonNull AppSearchImpl appSearchImpl, + @NonNull VisibilityStore visibilityStore) { + mLogger = Objects.requireNonNull(logger); + mAppSearchImpl = Objects.requireNonNull(appSearchImpl); + mVisibilityStore = Objects.requireNonNull(visibilityStore); + } + + @NonNull + public PlatformLogger getLogger() { + return mLogger; + } + + @NonNull + public AppSearchImpl getAppSearchImpl() { + return mAppSearchImpl; + } + + @NonNull + public VisibilityStore getVisibilityStore() { + return mVisibilityStore; + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java new file mode 100644 index 000000000000..cedc364f6072 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch; + +import android.annotation.NonNull; +import android.app.appsearch.exceptions.AppSearchException; +import android.content.Context; +import android.os.Environment; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.appsearch.external.localstorage.AppSearchImpl; +import com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy; +import com.android.server.appsearch.external.localstorage.stats.InitializeStats; +import com.android.server.appsearch.stats.PlatformLogger; +import com.android.server.appsearch.visibilitystore.VisibilityStore; + +import java.io.File; +import java.util.Map; +import java.util.Objects; + +/** + * Manages the lifecycle of AppSearch classes that should only be initialized once per device-user + * and make up the core of the AppSearch system. + * + * @hide + */ +public final class AppSearchUserInstanceManager { + private static final String TAG = "AppSearchUserInstanceMa"; + + private static volatile AppSearchUserInstanceManager sAppSearchUserInstanceManager; + + @GuardedBy("mInstancesLocked") + private final Map<UserHandle, AppSearchUserInstance> mInstancesLocked = new ArrayMap<>(); + + private AppSearchUserInstanceManager() {} + + /** + * Gets an instance of AppSearchUserInstanceManager to be used. + * + * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the + * existing instance will be returned. + */ + @NonNull + public static AppSearchUserInstanceManager getInstance() { + if (sAppSearchUserInstanceManager == null) { + synchronized (AppSearchUserInstanceManager.class) { + if (sAppSearchUserInstanceManager == null) { + sAppSearchUserInstanceManager = new AppSearchUserInstanceManager(); + } + } + } + return sAppSearchUserInstanceManager; + } + + /** + * Returns AppSearch directory in the credential encrypted system directory for the given user. + * + * <p>This folder should only be accessed after unlock. + */ + public static File getAppSearchDir(@NonNull UserHandle userHandle) { + // Duplicates the implementation of Environment#getDataSystemCeDirectory + // TODO(b/191059409): Unhide Environment#getDataSystemCeDirectory and switch to it. + File systemCeDir = new File(Environment.getDataDirectory(), "system_ce"); + File systemCeUserDir = new File(systemCeDir, String.valueOf(userHandle.getIdentifier())); + return new File(systemCeUserDir, "appsearch"); + } + + /** + * Gets an instance of AppSearchUserInstance for the given user, or creates one if none exists. + * + * <p>If no AppSearchUserInstance exists for the unlocked user, Icing will be initialized and + * one will be created. + * + * @param context The context + * @param userHandle The multi-user handle of the device user calling AppSearch + * @param config Flag manager for AppSearch + * @return An initialized {@link AppSearchUserInstance} for this user + */ + @NonNull + public AppSearchUserInstance getOrCreateUserInstance( + @NonNull Context context, + @NonNull UserHandle userHandle, + @NonNull AppSearchConfig config) + throws AppSearchException { + Objects.requireNonNull(context); + Objects.requireNonNull(userHandle); + Objects.requireNonNull(config); + + synchronized (mInstancesLocked) { + AppSearchUserInstance instance = mInstancesLocked.get(userHandle); + if (instance == null) { + Context userContext = context.createContextAsUser(userHandle, /*flags=*/ 0); + instance = createUserInstance(userContext, userHandle, config); + mInstancesLocked.put(userHandle, instance); + } + return instance; + } + } + + /** + * Closes and removes an {@link AppSearchUserInstance} for the given user. + * + * <p>All mutations applied to the underlying {@link AppSearchImpl} will be persisted to disk. + * + * @param userHandle The multi-user user handle of the user that need to be removed. + */ + public void closeAndRemoveUserInstance(@NonNull UserHandle userHandle) { + Objects.requireNonNull(userHandle); + synchronized (mInstancesLocked) { + AppSearchUserInstance instance = mInstancesLocked.remove(userHandle); + if (instance != null) { + instance.getAppSearchImpl().close(); + } + } + } + + /** + * Gets an {@link AppSearchUserInstance} for the given user. + * + * <p>This method should only be called by an initialized SearchSession, which has already + * called {@link #getOrCreateUserInstance} before. + * + * @param userHandle The multi-user handle of the device user calling AppSearch + * @return An initialized {@link AppSearchUserInstance} for this user + * @throws IllegalStateException if {@link AppSearchUserInstance} haven't created for the given + * user. + */ + @NonNull + public AppSearchUserInstance getUserInstance(@NonNull UserHandle userHandle) { + Objects.requireNonNull(userHandle); + synchronized (mInstancesLocked) { + AppSearchUserInstance instance = mInstancesLocked.get(userHandle); + if (instance == null) { + // Impossible scenario, user cannot call an uninitialized SearchSession, + // getInstance should always find the instance for the given user and never try to + // create an instance for this user again. + throw new IllegalStateException( + "AppSearchUserInstance has never been created for: " + userHandle); + } + return instance; + } + } + + @NonNull + private AppSearchUserInstance createUserInstance( + @NonNull Context userContext, + @NonNull UserHandle userHandle, + @NonNull AppSearchConfig config) + throws AppSearchException { + long totalLatencyStartMillis = SystemClock.elapsedRealtime(); + InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); + + // Initialize the classes that make up AppSearchUserInstance + PlatformLogger logger = new PlatformLogger(userContext, userHandle, config); + + File appSearchDir = getAppSearchDir(userHandle); + File icingDir = new File(appSearchDir, "icing"); + Log.i(TAG, "Creating new AppSearch instance at: " + icingDir); + AppSearchImpl appSearchImpl = + AppSearchImpl.create(icingDir, initStatsBuilder, new FrameworkOptimizeStrategy()); + + long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime(); + VisibilityStore visibilityStore = VisibilityStore.create(appSearchImpl, userContext); + long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime(); + + initStatsBuilder + .setTotalLatencyMillis( + (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) + .setPrepareVisibilityStoreLatencyMillis( + (int) + (prepareVisibilityStoreLatencyEndMillis + - prepareVisibilityStoreLatencyStartMillis)); + logger.logStats(initStatsBuilder.build()); + + return new AppSearchUserInstance(logger, appSearchImpl, visibilityStore); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java deleted file mode 100644 index e7845d51568d..000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch; - - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.exceptions.AppSearchException; -import android.content.Context; -import android.os.Environment; -import android.os.UserHandle; -import android.util.ArrayMap; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.appsearch.external.localstorage.AppSearchImpl; -import com.android.server.appsearch.external.localstorage.AppSearchLogger; - -import java.io.File; -import java.util.Map; -import java.util.Objects; - -/** - * Manages the lifecycle of instances of {@link AppSearchImpl}. - * - * <p>These instances are managed per unique device-user. - */ -public final class ImplInstanceManager { - private static final String APP_SEARCH_DIR = "appSearch"; - - private static ImplInstanceManager sImplInstanceManager; - - @GuardedBy("mInstancesLocked") - private final Map<UserHandle, AppSearchImpl> mInstancesLocked = new ArrayMap<>(); - - /** - * Gets an instance of ImplInstanceManager to be used. - * - * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the - * existing instance will be returned. - */ - @NonNull - public static ImplInstanceManager getInstance(@NonNull Context context) { - if (sImplInstanceManager == null) { - synchronized (ImplInstanceManager.class) { - if (sImplInstanceManager == null) { - sImplInstanceManager = new ImplInstanceManager(); - } - } - } - return sImplInstanceManager; - } - - /** - * Returns AppSearch directory in the credential encrypted system directory for the given user. - * - * <p>This folder should only be accessed after unlock. - */ - public static File getAppSearchDir(@NonNull UserHandle userHandle) { - return new File( - Environment.getDataSystemCeDirectory(userHandle.getIdentifier()), APP_SEARCH_DIR); - } - - /** - * Gets an instance of AppSearchImpl for the given user, or creates one if none exists. - * - * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and - * one will be created. - * - * @param context The system context - * @param userHandle The multi-user handle of the device user calling AppSearch - * @return An initialized {@link AppSearchImpl} for this user - */ - @NonNull - public AppSearchImpl getOrCreateAppSearchImpl( - @NonNull Context context, - @NonNull UserHandle userHandle, - @Nullable AppSearchLogger logger) throws AppSearchException { - Objects.requireNonNull(context); - Objects.requireNonNull(userHandle); - - synchronized (mInstancesLocked) { - AppSearchImpl instance = mInstancesLocked.get(userHandle); - if (instance == null) { - Context userContext = context.createContextAsUser(userHandle, /*flags=*/ 0); - instance = createImpl(userContext, userHandle, logger); - mInstancesLocked.put(userHandle, instance); - } - return instance; - } - } - - /** - * Close and remove an instance of {@link AppSearchImpl} for the given user. - * - * <p>All mutation apply to this {@link AppSearchImpl} will be persisted to disk. - * - * @param userHandle The multi-user user handle of the user that need to be removed. - */ - public void closeAndRemoveAppSearchImplForUser(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - AppSearchImpl appSearchImpl = mInstancesLocked.get(userHandle); - if (appSearchImpl != null) { - appSearchImpl.close(); - mInstancesLocked.remove(userHandle); - } - } - } - - /** - * Gets an instance of AppSearchImpl for the given user. - * - * <p>This method should only be called by an initialized SearchSession, which has been already - * created the AppSearchImpl instance for the given user. - * - * @param userHandle The multi-user handle of the device user calling AppSearch - * @return An initialized {@link AppSearchImpl} for this user - * @throws IllegalStateException if {@link AppSearchImpl} haven't created for the given user. - */ - @NonNull - public AppSearchImpl getAppSearchImpl(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - AppSearchImpl instance = mInstancesLocked.get(userHandle); - if (instance == null) { - // Impossible scenario, user cannot call an uninitialized SearchSession, - // getInstance should always find the instance for the given user and never try to - // create an instance for this user again. - throw new IllegalStateException( - "AppSearchImpl has never been created for: " + userHandle); - } - return instance; - } - } - - private AppSearchImpl createImpl( - @NonNull Context userContext, - @NonNull UserHandle userHandle, - @Nullable AppSearchLogger logger) - throws AppSearchException { - File appSearchDir = getAppSearchDir(userHandle); - return AppSearchImpl.create(appSearchDir, userContext, /*logger=*/ null); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index 29cb57c05eeb..77a1bb402bd3 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -39,7 +39,6 @@ import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.StorageInfo; import android.app.appsearch.exceptions.AppSearchException; import android.app.appsearch.util.LogUtil; -import android.content.Context; import android.os.Bundle; import android.os.SystemClock; import android.util.ArrayMap; @@ -145,21 +144,18 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; public final class AppSearchImpl implements Closeable { private static final String TAG = "AppSearchImpl"; - @VisibleForTesting static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000; - @VisibleForTesting static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB @VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100; private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); private final LogUtil mLogUtil = new LogUtil(TAG); + private final OptimizeStrategy mOptimizeStrategy; + @GuardedBy("mReadWriteLock") @VisibleForTesting final IcingSearchEngine mIcingSearchEngineLocked; - @GuardedBy("mReadWriteLock") - private final VisibilityStore mVisibilityStoreLocked; - // This map contains schema types and SchemaTypeConfigProtos for all package-database // prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each // prefixed schema type to its respective SchemaTypeConfigProto. @@ -195,51 +191,27 @@ public final class AppSearchImpl implements Closeable { * <p>Instead, logger instance needs to be passed to each individual method, like create, query * and putDocument. * - * @param logger collects stats for initialization if provided. + * @param initStatsBuilder collects stats for initialization if provided. */ @NonNull public static AppSearchImpl create( @NonNull File icingDir, - @NonNull Context userContext, - @Nullable AppSearchLogger logger) + @Nullable InitializeStats.Builder initStatsBuilder, + @NonNull OptimizeStrategy optimizeStrategy) throws AppSearchException { - Objects.requireNonNull(icingDir); - Objects.requireNonNull(userContext); - - long totalLatencyStartMillis = SystemClock.elapsedRealtime(); - InitializeStats.Builder initStatsBuilder = null; - if (logger != null) { - initStatsBuilder = new InitializeStats.Builder(); - } - - AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir, userContext, initStatsBuilder); - - long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime(); - appSearchImpl.initializeVisibilityStore(); - long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime(); - - if (logger != null) { - initStatsBuilder - .setTotalLatencyMillis( - (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) - .setPrepareVisibilityStoreLatencyMillis( - (int) - (prepareVisibilityStoreLatencyEndMillis - - prepareVisibilityStoreLatencyStartMillis)); - logger.logStats(initStatsBuilder.build()); - } - - return appSearchImpl; + return new AppSearchImpl(icingDir, initStatsBuilder, optimizeStrategy); } /** @param initStatsBuilder collects stats for initialization if provided. */ private AppSearchImpl( @NonNull File icingDir, - @NonNull Context userContext, - @Nullable InitializeStats.Builder initStatsBuilder) + @Nullable InitializeStats.Builder initStatsBuilder, + @NonNull OptimizeStrategy optimizeStrategy) throws AppSearchException { - mReadWriteLock.writeLock().lock(); + Objects.requireNonNull(icingDir); + mOptimizeStrategy = Objects.requireNonNull(optimizeStrategy); + mReadWriteLock.writeLock().lock(); try { // We synchronize here because we don't want to call IcingSearchEngine.initialize() more // than once. It's unnecessary and can be a costly operation. @@ -253,8 +225,6 @@ public final class AppSearchImpl implements Closeable { "Constructing IcingSearchEngine, response", Objects.hashCode(mIcingSearchEngineLocked)); - mVisibilityStoreLocked = new VisibilityStore(this, userContext); - // The core initialization procedure. If any part of this fails, we bail into // resetLocked(), deleting all data (but hopefully allowing AppSearchImpl to come up). try { @@ -336,23 +306,6 @@ public final class AppSearchImpl implements Closeable { } } - /** - * Initialize the visibility store in AppSearchImpl. - * - * @throws AppSearchException on IcingSearchEngine error. - */ - void initializeVisibilityStore() throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - mLogUtil.piiTrace("Initializing VisibilityStore, request"); - mVisibilityStoreLocked.initialize(); - mLogUtil.piiTrace("Initializing VisibilityStore, response"); - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - @GuardedBy("mReadWriteLock") private void throwIfClosedLocked() { if (mClosedLocked) { @@ -393,6 +346,8 @@ public final class AppSearchImpl implements Closeable { * @param packageName The package name that owns the schemas. * @param databaseName The name of the database where this schema lives. * @param schemas Schemas to set for this app. + * @param visibilityStore If set, {@code schemasNotPlatformSurfaceable} and {@code + * schemasPackageAccessible} will be saved here if the schema is successfully applied. * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform * surfaces. * @param schemasPackageAccessible Schema types that are visible to the specified packages. @@ -410,6 +365,7 @@ public final class AppSearchImpl implements Closeable { @NonNull String packageName, @NonNull String databaseName, @NonNull List<AppSearchSchema> schemas, + @Nullable VisibilityStore visibilityStore, @NonNull List<String> schemasNotPlatformSurfaceable, @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible, boolean forceOverride, @@ -473,25 +429,27 @@ public final class AppSearchImpl implements Closeable { removeFromMap(mSchemaMapLocked, prefix, schemaType); } - Set<String> prefixedSchemasNotPlatformSurfaceable = - new ArraySet<>(schemasNotPlatformSurfaceable.size()); - for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) { - prefixedSchemasNotPlatformSurfaceable.add( - prefix + schemasNotPlatformSurfaceable.get(i)); - } + if (visibilityStore != null) { + Set<String> prefixedSchemasNotPlatformSurfaceable = + new ArraySet<>(schemasNotPlatformSurfaceable.size()); + for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) { + prefixedSchemasNotPlatformSurfaceable.add( + prefix + schemasNotPlatformSurfaceable.get(i)); + } - Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible = - new ArrayMap<>(schemasPackageAccessible.size()); - for (Map.Entry<String, List<PackageIdentifier>> entry : - schemasPackageAccessible.entrySet()) { - prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue()); - } + Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible = + new ArrayMap<>(schemasPackageAccessible.size()); + for (Map.Entry<String, List<PackageIdentifier>> entry : + schemasPackageAccessible.entrySet()) { + prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue()); + } - mVisibilityStoreLocked.setVisibility( - packageName, - databaseName, - prefixedSchemasNotPlatformSurfaceable, - prefixedSchemasPackageAccessible); + visibilityStore.setVisibility( + packageName, + databaseName, + prefixedSchemasNotPlatformSurfaceable, + prefixedSchemasPackageAccessible); + } return SetSchemaResponseToProtoConverter.toSetSchemaResponse( setSchemaResultProto, prefix); @@ -646,9 +604,7 @@ public final class AppSearchImpl implements Closeable { // Logging stats if (pStatsBuilder != null) { pStatsBuilder - .getGeneralStatsBuilder() - .setStatusCode(statusProtoToResultCode(putResultProto.getStatus())); - pStatsBuilder + .setStatusCode(statusProtoToResultCode(putResultProto.getStatus())) .setGenerateDocumentProtoLatencyMillis( (int) (generateDocumentProtoEndTimeMillis @@ -667,9 +623,8 @@ public final class AppSearchImpl implements Closeable { if (logger != null) { long totalEndTimeMillis = SystemClock.elapsedRealtime(); - pStatsBuilder - .getGeneralStatsBuilder() - .setTotalLatencyMillis((int) (totalEndTimeMillis - totalStartTimeMillis)); + pStatsBuilder.setTotalLatencyMillis( + (int) (totalEndTimeMillis - totalStartTimeMillis)); logger.logStats(pStatsBuilder.build()); } } @@ -812,8 +767,13 @@ public final class AppSearchImpl implements Closeable { * * @param queryExpression Query String to search. * @param searchSpec Spec for setting filters, raw query etc. - * @param callerPackageName Package name of the caller, should belong to the {@code callerUid}. + * @param callerPackageName Package name of the caller, should belong to the {@code + * userContext}. + * @param visibilityStore Optional visibility store to obtain system and package visibility + * settings from * @param callerUid UID of the client making the globalQuery call. + * @param callerHasSystemAccess Whether the caller has been positively identified as having + * access to schemas marked system surfaceable. * @param logger logger to collect globalQuery stats * @return The results of performing this search. It may contain an empty list of results if no * documents matched the query. @@ -824,7 +784,9 @@ public final class AppSearchImpl implements Closeable { @NonNull String queryExpression, @NonNull SearchSpec searchSpec, @NonNull String callerPackageName, + @Nullable VisibilityStore visibilityStore, int callerUid, + boolean callerHasSystemAccess, @Nullable AppSearchLogger logger) throws AppSearchException { long totalLatencyStartMillis = SystemClock.elapsedRealtime(); @@ -881,15 +843,18 @@ public final class AppSearchImpl implements Closeable { if (packageName.equals(callerPackageName)) { // Callers can always retrieve their own data allow = true; + } else if (visibilityStore == null) { + // If there's no visibility store, there's no extra access + allow = false; } else { String databaseName = getDatabaseName(prefixedSchema); allow = - mVisibilityStoreLocked.isSchemaSearchableByCaller( + visibilityStore.isSchemaSearchableByCaller( packageName, databaseName, prefixedSchema, - callerPackageName, - callerUid); + callerUid, + callerHasSystemAccess); } if (!allow) { @@ -1484,9 +1449,6 @@ public final class AppSearchImpl implements Closeable { /** * Clears documents and schema across all packages and databaseNames. * - * <p>This method also clear all data in {@link VisibilityStore}, an {@link - * #initializeVisibilityStore()} must be called after this. - * * <p>This method belongs to mutate group. * * @throws AppSearchException on IcingSearchEngine error. @@ -1510,9 +1472,6 @@ public final class AppSearchImpl implements Closeable { .setResetStatusCode(statusProtoToResultCode(resetResultProto.getStatus())); } - // Must be called after everything else since VisibilityStore may repopulate - // IcingSearchEngine with an initial schema. - mVisibilityStoreLocked.handleReset(); checkSuccess(resetResultProto.getStatus()); } @@ -2001,7 +1960,7 @@ public final class AppSearchImpl implements Closeable { * resources that could be released. * * <p>{@link IcingSearchEngine#optimize()} should be called only if {@link - * GetOptimizeInfoResultProto} shows there is enough resources could be released. + * OptimizeStrategy#shouldOptimize(GetOptimizeInfoResultProto)} return true. */ public void checkForOptimize() throws AppSearchException { mReadWriteLock.writeLock().lock(); @@ -2009,9 +1968,7 @@ public final class AppSearchImpl implements Closeable { GetOptimizeInfoResultProto optimizeInfo = getOptimizeInfoResultLocked(); checkSuccess(optimizeInfo.getStatus()); mOptimizeIntervalCountLocked = 0; - // Second threshold, decide when to call optimize(). - if (optimizeInfo.getOptimizableDocs() >= OPTIMIZE_THRESHOLD_DOC_COUNT - || optimizeInfo.getEstimatedOptimizableBytes() >= OPTIMIZE_THRESHOLD_BYTES) { + if (mOptimizeStrategy.shouldOptimize(optimizeInfo)) { optimize(); } } finally { @@ -2022,11 +1979,7 @@ public final class AppSearchImpl implements Closeable { // go/icing-library-apis. } - /** - * Triggers {@link IcingSearchEngine#optimize()} directly. - * - * <p>This method should be only called as a scheduled task in AppSearch Platform backend. - */ + /** Triggers {@link IcingSearchEngine#optimize()} directly. */ public void optimize() throws AppSearchException { mReadWriteLock.writeLock().lock(); try { @@ -2077,13 +2030,6 @@ public final class AppSearchImpl implements Closeable { return result; } - @GuardedBy("mReadWriteLock") - @NonNull - @VisibleForTesting - VisibilityStore getVisibilityStoreLocked() { - return mVisibilityStoreLocked; - } - /** * Converts an erroneous status code from the Icing status enums to the AppSearchResult enums. * diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategy.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategy.java new file mode 100644 index 000000000000..8ec30e186306 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategy.java @@ -0,0 +1,44 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch.external.localstorage; + +import android.annotation.NonNull; + +import com.android.internal.annotations.VisibleForTesting; + +import com.google.android.icing.proto.GetOptimizeInfoResultProto; + +/** + * An implementation of {@link OptimizeStrategy} will determine when to trigger {@link + * AppSearchImpl#optimize()} in Jetpack environment. + * + * @hide + */ +public class FrameworkOptimizeStrategy implements OptimizeStrategy { + + @VisibleForTesting static final int DOC_COUNT_OPTIMIZE_THRESHOLD = 100_000; + @VisibleForTesting static final int BYTES_OPTIMIZE_THRESHOLD = 1 * 1024 * 1024 * 1024; // 1GB + + @VisibleForTesting + static final long TIME_OPTIMIZE_THRESHOLD_MILLIS = 7 * 24 * 60 * 60 * 1000; // 1 week + + @Override + public boolean shouldOptimize(@NonNull GetOptimizeInfoResultProto optimizeInfo) { + return optimizeInfo.getOptimizableDocs() >= DOC_COUNT_OPTIMIZE_THRESHOLD + || optimizeInfo.getEstimatedOptimizableBytes() >= BYTES_OPTIMIZE_THRESHOLD + || optimizeInfo.getTimeSinceLastOptimizeMs() >= TIME_OPTIMIZE_THRESHOLD_MILLIS; + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/OptimizeStrategy.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/OptimizeStrategy.java new file mode 100644 index 000000000000..6cb84bc64eb9 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/OptimizeStrategy.java @@ -0,0 +1,39 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localstorage; + +import android.annotation.NonNull; + +import com.google.android.icing.proto.GetOptimizeInfoResultProto; + +/** + * An interface class for implementing a strategy to determine when to trigger {@link + * AppSearchImpl#optimize()}. + * + * @hide + */ +public interface OptimizeStrategy { + + /** + * Determines whether {@link AppSearchImpl#optimize()} need to be triggered to release garbage + * resources in AppSearch base on the given information. + * + * @param optimizeInfo The proto object indicates the number of garbage resources in AppSearch. + * @return {@code true} if {@link AppSearchImpl#optimize()} need to be triggered. + */ + boolean shouldOptimize(@NonNull GetOptimizeInfoResultProto optimizeInfo); +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java index ea5263aa9aa5..81fb418a6a0a 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java @@ -18,6 +18,8 @@ package com.android.server.appsearch.external.localstorage.stats; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.appsearch.AppSearchResult; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -29,9 +31,9 @@ import java.util.Objects; * <p>This class can set which stats to log for both batch and non-batch {@link * android.app.appsearch.AppSearchSession} calls. * - * <p>Some function calls like {@link android.app.appsearch.AppSearchSession#setSchema} have their - * own detailed stats class {@link placeholder}. However, {@link CallStats} can still be used along - * with the detailed stats class for easy aggregation/analysis with other function calls. + * <p>Some function calls may have their own detailed stats class like {@link PutDocumentStats}. + * However, {@link CallStats} can still be used along with the detailed stats class for easy + * aggregation/analysis with other function calls. * * @hide */ @@ -73,7 +75,16 @@ public class CallStats { public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH = 13; public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH = 14; - @NonNull private final GeneralStats mGeneralStats; + @Nullable private final String mPackageName; + @Nullable private final String mDatabase; + /** + * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal + * state. + */ + @AppSearchResult.ResultCode private final int mStatusCode; + + private final int mTotalLatencyMillis; + @CallType private final int mCallType; private final int mEstimatedBinderLatencyMillis; private final int mNumOperationsSucceeded; @@ -81,17 +92,37 @@ public class CallStats { CallStats(@NonNull Builder builder) { Objects.requireNonNull(builder); - mGeneralStats = Objects.requireNonNull(builder.mGeneralStatsBuilder).build(); + mPackageName = builder.mPackageName; + mDatabase = builder.mDatabase; + mStatusCode = builder.mStatusCode; + mTotalLatencyMillis = builder.mTotalLatencyMillis; mCallType = builder.mCallType; mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis; mNumOperationsSucceeded = builder.mNumOperationsSucceeded; mNumOperationsFailed = builder.mNumOperationsFailed; } - /** Returns general information for the call. */ - @NonNull - public GeneralStats getGeneralStats() { - return mGeneralStats; + /** Returns calling package name. */ + @Nullable + public String getPackageName() { + return mPackageName; + } + + /** Returns calling database name. */ + @Nullable + public String getDatabase() { + return mDatabase; + } + + /** Returns status code for this api call. */ + @AppSearchResult.ResultCode + public int getStatusCode() { + return mStatusCode; + } + + /** Returns total latency of this api call in millis. */ + public int getTotalLatencyMillis() { + return mTotalLatencyMillis; } /** Returns type of the call. */ @@ -137,23 +168,41 @@ public class CallStats { /** Builder for {@link CallStats}. */ public static class Builder { - @NonNull final GeneralStats.Builder mGeneralStatsBuilder; + @Nullable String mPackageName; + @Nullable String mDatabase; + @AppSearchResult.ResultCode int mStatusCode; + int mTotalLatencyMillis; @CallType int mCallType; int mEstimatedBinderLatencyMillis; int mNumOperationsSucceeded; int mNumOperationsFailed; - /** Builder takes {@link GeneralStats.Builder}. */ - public Builder(@NonNull String packageName, @NonNull String database) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(database); - mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database); + /** Sets the PackageName used by the session. */ + @NonNull + public Builder setPackageName(@NonNull String packageName) { + mPackageName = Objects.requireNonNull(packageName); + return this; + } + + /** Sets the database used by the session. */ + @NonNull + public Builder setDatabase(@NonNull String database) { + mDatabase = Objects.requireNonNull(database); + return this; } - /** Returns {@link GeneralStats.Builder}. */ + /** Sets the status code. */ @NonNull - public GeneralStats.Builder getGeneralStatsBuilder() { - return mGeneralStatsBuilder; + public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { + mStatusCode = statusCode; + return this; + } + + /** Sets total latency in millis. */ + @NonNull + public Builder setTotalLatencyMillis(int totalLatencyMillis) { + mTotalLatencyMillis = totalLatencyMillis; + return this; } /** Sets type of the call. */ diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java deleted file mode 100644 index 53c1ee3f675f..000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.stats; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; - -import java.util.Objects; - -/** - * A class for holding general logging information. - * - * <p>This class cannot be logged by {@link - * com.android.server.appsearch.external.localstorage.AppSearchLogger} directly. It is used for - * defining general logging information that is shared across different stats classes. - * - * @see PutDocumentStats - * @see CallStats - * @hide - */ -public final class GeneralStats { - /** Package name of the application. */ - @NonNull private final String mPackageName; - - /** Database name within AppSearch. */ - @NonNull private final String mDatabase; - - /** - * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal - * state. - */ - @AppSearchResult.ResultCode private final int mStatusCode; - - private final int mTotalLatencyMillis; - - GeneralStats(@NonNull Builder builder) { - Objects.requireNonNull(builder); - mPackageName = Objects.requireNonNull(builder.mPackageName); - mDatabase = Objects.requireNonNull(builder.mDatabase); - mStatusCode = builder.mStatusCode; - mTotalLatencyMillis = builder.mTotalLatencyMillis; - } - - /** Returns package name. */ - @NonNull - public String getPackageName() { - return mPackageName; - } - - /** Returns database name. */ - @NonNull - public String getDatabase() { - return mDatabase; - } - - /** Returns result code from {@link AppSearchResult#getResultCode()} */ - @AppSearchResult.ResultCode - public int getStatusCode() { - return mStatusCode; - } - - /** Returns total latency, in milliseconds. */ - public int getTotalLatencyMillis() { - return mTotalLatencyMillis; - } - - /** Builder for {@link GeneralStats}. */ - public static class Builder { - @NonNull final String mPackageName; - @NonNull final String mDatabase; - @AppSearchResult.ResultCode int mStatusCode = AppSearchResult.RESULT_UNKNOWN_ERROR; - int mTotalLatencyMillis; - - /** - * Constructor - * - * @param packageName name of the package logging stats - * @param database name of the database logging stats - */ - public Builder(@NonNull String packageName, @NonNull String database) { - mPackageName = Objects.requireNonNull(packageName); - mDatabase = Objects.requireNonNull(database); - } - - /** Sets status code returned from {@link AppSearchResult#getResultCode()} */ - @NonNull - public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { - mStatusCode = statusCode; - return this; - } - - /** Sets total latency, in milliseconds. */ - @NonNull - public Builder setTotalLatencyMillis(int totalLatencyMillis) { - mTotalLatencyMillis = totalLatencyMillis; - return this; - } - - /** - * Creates a new {@link GeneralStats} object from the contents of this {@link Builder} - * instance. - */ - @NonNull - public GeneralStats build() { - return new GeneralStats(/* builder= */ this); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java index d031172d29c1..7ba181668bfd 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java @@ -17,6 +17,7 @@ package com.android.server.appsearch.external.localstorage.stats; import android.annotation.NonNull; +import android.app.appsearch.AppSearchResult; import java.util.Objects; @@ -27,8 +28,15 @@ import java.util.Objects; * @hide */ public final class PutDocumentStats { - /** {@link GeneralStats} holds the general stats. */ - @NonNull private final GeneralStats mGeneralStats; + @NonNull private final String mPackageName; + @NonNull private final String mDatabase; + /** + * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal + * state. + */ + @AppSearchResult.ResultCode private final int mStatusCode; + + private final int mTotalLatencyMillis; /** Time used to generate a document proto from a Bundle. */ private final int mGenerateDocumentProtoLatencyMillis; @@ -61,7 +69,10 @@ public final class PutDocumentStats { PutDocumentStats(@NonNull Builder builder) { Objects.requireNonNull(builder); - mGeneralStats = Objects.requireNonNull(builder.mGeneralStatsBuilder).build(); + mPackageName = builder.mPackageName; + mDatabase = builder.mDatabase; + mStatusCode = builder.mStatusCode; + mTotalLatencyMillis = builder.mTotalLatencyMillis; mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis; mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis; mNativeLatencyMillis = builder.mNativeLatencyMillis; @@ -73,10 +84,27 @@ public final class PutDocumentStats { mNativeExceededMaxNumTokens = builder.mNativeExceededMaxNumTokens; } - /** Returns the {@link GeneralStats} object attached to this instance. */ + /** Returns calling package name. */ + @NonNull + public String getPackageName() { + return mPackageName; + } + + /** Returns calling database name. */ @NonNull - public GeneralStats getGeneralStats() { - return mGeneralStats; + public String getDatabase() { + return mDatabase; + } + + /** Returns status code for this putDocument. */ + @AppSearchResult.ResultCode + public int getStatusCode() { + return mStatusCode; + } + + /** Returns total latency of this putDocument in millis. */ + public int getTotalLatencyMillis() { + return mTotalLatencyMillis; } /** Returns time spent on generating document proto, in milliseconds. */ @@ -129,7 +157,10 @@ public final class PutDocumentStats { /** Builder for {@link PutDocumentStats}. */ public static class Builder { - @NonNull final GeneralStats.Builder mGeneralStatsBuilder; + @NonNull final String mPackageName; + @NonNull final String mDatabase; + @AppSearchResult.ResultCode int mStatusCode; + int mTotalLatencyMillis; int mGenerateDocumentProtoLatencyMillis; int mRewriteDocumentTypesLatencyMillis; int mNativeLatencyMillis; @@ -140,17 +171,24 @@ public final class PutDocumentStats { int mNativeNumTokensIndexed; boolean mNativeExceededMaxNumTokens; - /** Builder takes {@link GeneralStats.Builder}. */ + /** Builder for {@link PutDocumentStats} */ public Builder(@NonNull String packageName, @NonNull String database) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(database); - mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database); + mPackageName = Objects.requireNonNull(packageName); + mDatabase = Objects.requireNonNull(database); } - /** Returns {@link GeneralStats.Builder}. */ + /** Sets the status code. */ @NonNull - public GeneralStats.Builder getGeneralStatsBuilder() { - return mGeneralStatsBuilder; + public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { + mStatusCode = statusCode; + return this; + } + + /** Sets total latency in millis. */ + @NonNull + public Builder setTotalLatencyMillis(int totalLatencyMillis) { + mTotalLatencyMillis = totalLatencyMillis; + return this; } /** Sets how much time we spend for generating document proto, in milliseconds. */ diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java deleted file mode 100644 index ea00f506b47f..000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.stats; - -import android.annotation.NonNull; -import android.content.Context; -import android.os.UserHandle; -import android.util.ArrayMap; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.appsearch.AppSearchConfig; -import com.android.server.appsearch.AppSearchManagerService; - -import java.util.Map; -import java.util.Objects; - -/** - * Manages the lifecycle of instances of {@link PlatformLogger}. - * - * <p>These instances are managed per unique device-user. - */ -public final class LoggerInstanceManager { - private static volatile LoggerInstanceManager sLoggerInstanceManager; - - @GuardedBy("mInstancesLocked") - private final Map<UserHandle, PlatformLogger> mInstancesLocked = new ArrayMap<>(); - - private LoggerInstanceManager() { - } - - /** - * Gets an instance of {@link LoggerInstanceManager} to be used. - * - * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the - * existing instance will be returned. - */ - @NonNull - public static LoggerInstanceManager getInstance() { - if (sLoggerInstanceManager == null) { - synchronized (LoggerInstanceManager.class) { - if (sLoggerInstanceManager == null) { - sLoggerInstanceManager = - new LoggerInstanceManager(); - } - } - } - return sLoggerInstanceManager; - } - - /** - * Gets an instance of PlatformLogger for the given user, or creates one if none exists. - * - * @param context The context - * @param userHandle The multi-user handle of the device user calling AppSearch - * @return An initialized {@link PlatformLogger} for this user - */ - @NonNull - public PlatformLogger getOrCreatePlatformLogger( - @NonNull Context context, @NonNull UserHandle userHandle, - @NonNull AppSearchConfig config) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - PlatformLogger instance = mInstancesLocked.get(userHandle); - if (instance == null) { - instance = new PlatformLogger(context, userHandle, config); - mInstancesLocked.put(userHandle, instance); - } - return instance; - } - } - - /** - * Gets an instance of PlatformLogger for the given user. - * - * <p>This method should only be called by an initialized SearchSession, which has been already - * created the PlatformLogger instance for the given user. - * - * @param userHandle The multi-user handle of the device user calling AppSearch - * @return An initialized {@link PlatformLogger} for this user - * @throws IllegalStateException if {@link PlatformLogger} haven't created for the given user. - */ - @NonNull - public PlatformLogger getPlatformLogger(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - PlatformLogger instance = mInstancesLocked.get(userHandle); - if (instance == null) { - // Impossible scenario, user cannot call an uninitialized SearchSession, - // getInstance should always find the instance for the given user and never try to - // create an instance for this user again. - throw new IllegalStateException( - "PlatformLogger has never been created for: " + userHandle); - } - return instance; - } - } - - /** - * Remove an instance of {@link PlatformLogger} for the given user. - * - * <p>This method should only be called if {@link AppSearchManagerService} receives an - * ACTION_USER_REMOVED, which the logger instance of given user should be removed. - * - * @param userHandle The multi-user handle of the user that need to be removed. - */ - public void removePlatformLoggerForUser(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - mInstancesLocked.remove(userHandle); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java index 5a0199f8d78f..31fead5e6314 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java @@ -192,9 +192,8 @@ public final class PlatformLogger implements AppSearchLogger { @GuardedBy("mLock") private void logStatsImplLocked(@NonNull CallStats stats) { mLastPushTimeMillisLocked = SystemClock.elapsedRealtime(); - ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(), - stats.getCallType()); - String database = stats.getGeneralStats().getDatabase(); + ExtraStats extraStats = createExtraStatsLocked(stats.getPackageName(), stats.getCallType()); + String database = stats.getDatabase(); try { int hashCodeForDatabase = calculateHashCodeMd5(database); AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_CALL_STATS_REPORTED, @@ -202,8 +201,8 @@ public final class PlatformLogger implements AppSearchLogger { extraStats.mSkippedSampleCount, extraStats.mPackageUid, hashCodeForDatabase, - stats.getGeneralStats().getStatusCode(), - stats.getGeneralStats().getTotalLatencyMillis(), + stats.getStatusCode(), + stats.getTotalLatencyMillis(), stats.getCallType(), stats.getEstimatedBinderLatencyMillis(), stats.getNumOperationsSucceeded(), @@ -224,9 +223,9 @@ public final class PlatformLogger implements AppSearchLogger { @GuardedBy("mLock") private void logStatsImplLocked(@NonNull PutDocumentStats stats) { mLastPushTimeMillisLocked = SystemClock.elapsedRealtime(); - ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(), - CallStats.CALL_TYPE_PUT_DOCUMENT); - String database = stats.getGeneralStats().getDatabase(); + ExtraStats extraStats = createExtraStatsLocked( + stats.getPackageName(), CallStats.CALL_TYPE_PUT_DOCUMENT); + String database = stats.getDatabase(); try { int hashCodeForDatabase = calculateHashCodeMd5(database); AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_PUT_DOCUMENT_STATS_REPORTED, @@ -234,8 +233,8 @@ public final class PlatformLogger implements AppSearchLogger { extraStats.mSkippedSampleCount, extraStats.mPackageUid, hashCodeForDatabase, - stats.getGeneralStats().getStatusCode(), - stats.getGeneralStats().getTotalLatencyMillis(), + stats.getStatusCode(), + stats.getTotalLatencyMillis(), stats.getGenerateDocumentProtoLatencyMillis(), stats.getRewriteDocumentTypesLatencyMillis(), stats.getNativeLatencyMillis(), diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java index 5ad4276d9318..9e36fd02569f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java @@ -73,9 +73,4 @@ class NotPlatformSurfaceableMap { // isn't one of those opt-outs, it's surfaceable. return !schemaTypes.contains(prefixedSchema); } - - /** Discards all data in the map. */ - public void clear() { - mMap.clear(); - } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java index 2b3934718aed..cff729aa4e8a 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java @@ -82,9 +82,4 @@ class PackageAccessibleMap { } return accessiblePackages; } - - /** Discards all data in the map. */ - public void clear() { - mMap.clear(); - } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java index 95ed36898c73..ae1ec56b4ee6 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java @@ -66,8 +66,8 @@ import java.util.Set; * @hide */ public class VisibilityStore { - /** No-op user id that won't have any visibility settings. */ - public static final int NO_OP_USER_ID = -1; + /** No-op uid that won't have any visibility settings. */ + public static final int NO_OP_UID = -1; /** Version for the visibility schema */ private static final int SCHEMA_VERSION = 0; @@ -76,7 +76,7 @@ public class VisibilityStore { * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}. */ - @VisibleForTesting public static final String PACKAGE_NAME = "VS#Pkg"; + public static final String PACKAGE_NAME = "VS#Pkg"; @VisibleForTesting public static final String DATABASE_NAME = "VS#Db"; @@ -99,28 +99,23 @@ public class VisibilityStore { private final PackageAccessibleMap mPackageAccessibleMap = new PackageAccessibleMap(); /** - * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()} - * before using the object. + * Creates and initializes VisibilityStore. * * @param appSearchImpl AppSearchImpl instance * @param userContext Context of the user that the call is being made as */ - public VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) { - mAppSearchImpl = appSearchImpl; - mUserContext = Objects.requireNonNull(userContext); + @NonNull + public static VisibilityStore create( + @NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) + throws AppSearchException { + return new VisibilityStore(appSearchImpl, userContext); } - /** - * Initializes schemas and member variables to track visibility settings. - * - * <p>This is kept separate from the constructor because this will call methods on - * AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example, - * {@link AppSearchImpl#setSchema} will call {@link #setVisibility}. We need to have both - * AppSearchImpl and VisibilityStore fully initialized for this call flow to work. - * - * @throws AppSearchException AppSearchException on AppSearchImpl error. - */ - public void initialize() throws AppSearchException { + private VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) + throws AppSearchException { + mAppSearchImpl = Objects.requireNonNull(appSearchImpl); + mUserContext = Objects.requireNonNull(userContext); + GetSchemaResponse getSchemaResponse = mAppSearchImpl.getSchema(PACKAGE_NAME, DATABASE_NAME); boolean hasVisibilityType = false; boolean hasPackageAccessibleType = false; @@ -142,6 +137,7 @@ public class VisibilityStore { PACKAGE_NAME, DATABASE_NAME, Arrays.asList(VisibilityDocument.SCHEMA, PackageAccessibleDocument.SCHEMA), + /*visibilityStore=*/ null, // Avoid recursive calls /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -149,7 +145,6 @@ public class VisibilityStore { } // Populate visibility settings set - mNotPlatformSurfaceableMap.clear(); for (Map.Entry<String, Set<String>> entry : mAppSearchImpl.getPackageToDatabases().entrySet()) { String packageName = entry.getKey(); @@ -281,38 +276,45 @@ public class VisibilityStore { } /** + * Checks whether the given package has access to system-surfaceable schemas. + * + * @param callerPackageName Package name of the caller. + */ + public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) { + Objects.requireNonNull(callerPackageName); + return mUserContext.getPackageManager() + .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, callerPackageName) + == PackageManager.PERMISSION_GRANTED; + } + + /** * Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}. * * @param packageName Package that owns the schema. * @param databaseName Database within the package that owns the schema. * @param prefixedSchema Prefixed schema type the caller is trying to access. - * @param callerPackageName Package name of the caller. - * @param callerUid Uid of the caller. + * @param callerUid UID of the client making the globalQuery call. + * @param callerHasSystemAccess Whether the caller has been identified as having + * access to schemas marked system surfaceable by {@link + * #doesCallerHaveSystemAccess}. */ public boolean isSchemaSearchableByCaller( @NonNull String packageName, @NonNull String databaseName, @NonNull String prefixedSchema, - @NonNull String callerPackageName, - int callerUid) { + int callerUid, + boolean callerHasSystemAccess) { Objects.requireNonNull(packageName); Objects.requireNonNull(databaseName); Objects.requireNonNull(prefixedSchema); - Objects.requireNonNull(callerPackageName); if (packageName.equals(PACKAGE_NAME)) { return false; // VisibilityStore schemas are for internal bookkeeping. } - // TODO(b/180058203): If we can cache or pass in that a caller has the - // READ_GLOBAL_SEARCH_DATA permission, then we can save this package manager lookup for - // each schema we may check in the loop. - if (mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable( - packageName, databaseName, prefixedSchema) - && mUserContext - .getPackageManager() - .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, callerPackageName) - == PackageManager.PERMISSION_GRANTED) { + if (callerHasSystemAccess + && mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable( + packageName, databaseName, prefixedSchema)) { return true; } @@ -373,16 +375,6 @@ public class VisibilityStore { } /** - * Handles an {@code AppSearchImpl#reset()} by clearing any cached state. - * - * <p>{@link #initialize()} must be called after this. - */ - public void handleReset() { - mNotPlatformSurfaceableMap.clear(); - mPackageAccessibleMap.clear(); - } - - /** * Adds a prefix to create a visibility store document's id. * * @param packageName Package to which the visibility doc refers diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 395292df120a..78d39cc6deac 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -c35ced970a63a6c7b1d17f9706160579540850d6 +31a54dba5bda4d0109ea91eb1ac047c937cbaae3 diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 1efe5cb2f53e..4843415fdbdd 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -1285,14 +1285,20 @@ public class AlarmManager { } /** - * Called to check if the caller has the permission - * {@link Manifest.permission#SCHEDULE_EXACT_ALARM}. - * - * Apps can start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to + * Called to check if the caller can schedule exact alarms. + * <p> + * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms if they + * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. These apps can also + * start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to * request this from the user. + * <p> + * Apps targeting lower sdk versions, can always schedule exact alarms. * - * @return {@code true} if the caller has the permission, {@code false} otherwise. + * @return {@code true} if the caller can schedule exact alarms. * @see android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM + * @see #setExact(int, long, PendingIntent) + * @see #setExactAndAllowWhileIdle(int, long, PendingIntent) + * @see #setAlarmClock(AlarmClockInfo, PendingIntent) */ public boolean canScheduleExactAlarms() { return hasScheduleExactAlarm(mContext.getOpPackageName(), mContext.getUserId()); diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index fe0c7f718bb0..70e548d4c547 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -2006,16 +2006,12 @@ public class AlarmManagerService extends SystemService { windowLength = INTERVAL_DAY; } else if ((flags & FLAG_PRIORITIZE) == 0 && windowLength < minAllowedWindow) { // Prioritized alarms are exempt from minimum window limits. - if (CompatChanges.isChangeEnabled( + if (!isExemptFromMinWindowRestrictions(callingUid) && CompatChanges.isChangeEnabled( AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, callingPackage, UserHandle.getUserHandleForUid(callingUid))) { Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to " + minAllowedWindow + "ms."); windowLength = minAllowedWindow; - } else { - // TODO (b/185199076): Remove temporary log to catch breaking apps. - Slog.wtf(TAG, "Short window " + windowLength + "ms specified by " - + callingPackage); } } maxElapsed = triggerElapsed + windowLength; @@ -2409,6 +2405,13 @@ public class AlarmManagerService extends SystemService { } /** + * Returns true if the given uid can set window to be as small as it wants. + */ + boolean isExemptFromMinWindowRestrictions(int uid) { + return isExemptFromExactAlarmPermission(uid); + } + + /** * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact, * allow-while-idle alarms. */ @@ -2569,6 +2572,9 @@ public class AlarmManagerService extends SystemService { throw new SecurityException("Uid " + callingUid + " cannot query hasScheduleExactAlarm for uid " + uid); } + if (!isExactAlarmChangeEnabled(packageName, userId)) { + return true; + } return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index f7415962cc7e..7a2840709d15 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -16,6 +16,9 @@ package com.android.server.job; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; +import static android.net.NetworkCapabilities.TRANSPORT_TEST; + import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; @@ -30,6 +33,7 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; +import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.AtomicFile; @@ -63,6 +67,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.StringJoiner; import java.util.function.Consumer; import java.util.function.Predicate; @@ -387,6 +392,36 @@ public final class JobStore { } /** + * Returns a single string representation of the contents of the specified intArray. + * If the intArray is [1, 2, 4] as the input, the return result will be the string "1,2,4". + */ + @VisibleForTesting + static String intArrayToString(int[] values) { + final StringJoiner sj = new StringJoiner(","); + for (final int value : values) { + sj.add(String.valueOf(value)); + } + return sj.toString(); + } + + + /** + * Converts a string containing a comma-separated list of decimal representations + * of ints into an array of int. If the string is not correctly formatted, + * or if any value doesn't fit into an int, NumberFormatException is thrown. + */ + @VisibleForTesting + static int[] stringToIntArray(String str) { + if (TextUtils.isEmpty(str)) return new int[0]; + final String[] arr = str.split(","); + final int[] values = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + values[i] = Integer.parseInt(arr[i]); + } + return values; + } + + /** * Runnable that writes {@link #mJobSet} out to xml. * NOTE: This Runnable locks on mLock */ @@ -549,15 +584,12 @@ public final class JobStore { out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS); if (jobStatus.hasConnectivityConstraint()) { final NetworkRequest network = jobStatus.getJob().getRequiredNetwork(); - // STOPSHIP b/183071974: improve the scheme for backward compatibility and - // mainline cleanliness. - out.attribute(null, "net-capabilities", Long.toString( - BitUtils.packBits(network.getCapabilities()))); - out.attribute(null, "net-unwanted-capabilities", Long.toString( - BitUtils.packBits(network.getForbiddenCapabilities()))); - - out.attribute(null, "net-transport-types", Long.toString( - BitUtils.packBits(network.getTransportTypes()))); + out.attribute(null, "net-capabilities-csv", intArrayToString( + network.getCapabilities())); + out.attribute(null, "net-forbidden-capabilities-csv", intArrayToString( + network.getForbiddenCapabilities())); + out.attribute(null, "net-transport-types-csv", intArrayToString( + network.getTransportTypes())); } if (jobStatus.hasIdleConstraint()) { out.attribute(null, "idle", Boolean.toString(true)); @@ -831,7 +863,14 @@ public final class JobStore { } catch (NumberFormatException e) { Slog.d(TAG, "Error reading constraints, skipping."); return null; + } catch (XmlPullParserException e) { + Slog.d(TAG, "Error Parser Exception.", e); + return null; + } catch (IOException e) { + Slog.d(TAG, "Error I/O Exception.", e); + return null; } + parser.next(); // Consume </constraints> // Read out execution parameters tag. @@ -973,31 +1012,79 @@ public final class JobStore { return new JobInfo.Builder(jobId, cname); } - private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { + /** + * In S, there has been a change in format to make the code more robust and more + * maintainable. + * If the capabities are bits 4, 14, 15, the format in R, it is a long string as + * netCapabilitiesLong = '49168' from the old XML file attribute "net-capabilities". + * The format in S is the int array string as netCapabilitiesIntArray = '4,14,15' + * from the new XML file attribute "net-capabilities-array". + * For backward compatibility, when reading old XML the old format is still supported in + * reading, but in order to avoid issues with OEM-defined flags, the accepted capabilities + * are limited to that(maxNetCapabilityInR & maxTransportInR) defined in R. + */ + private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) + throws XmlPullParserException, IOException { String val; + String netCapabilitiesLong = null; + String netForbiddenCapabilitiesLong = null; + String netTransportTypesLong = null; + + final String netCapabilitiesIntArray = parser.getAttributeValue( + null, "net-capabilities-csv"); + final String netForbiddenCapabilitiesIntArray = parser.getAttributeValue( + null, "net-forbidden-capabilities-csv"); + final String netTransportTypesIntArray = parser.getAttributeValue( + null, "net-transport-types-csv"); + if (netCapabilitiesIntArray == null || netTransportTypesIntArray == null) { + netCapabilitiesLong = parser.getAttributeValue(null, "net-capabilities"); + netForbiddenCapabilitiesLong = parser.getAttributeValue( + null, "net-unwanted-capabilities"); + netTransportTypesLong = parser.getAttributeValue(null, "net-transport-types"); + } - final String netCapabilities = parser.getAttributeValue(null, "net-capabilities"); - final String netforbiddenCapabilities = parser.getAttributeValue( - null, "net-unwanted-capabilities"); - final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types"); - if (netCapabilities != null && netTransportTypes != null) { + if ((netCapabilitiesIntArray != null) && (netTransportTypesIntArray != null)) { final NetworkRequest.Builder builder = new NetworkRequest.Builder() .clearCapabilities(); - final long forbiddenCapabilities = netforbiddenCapabilities != null - ? Long.parseLong(netforbiddenCapabilities) - : BitUtils.packBits(builder.build().getForbiddenCapabilities()); - // We're okay throwing NFE here; caught by caller - for (int capability : BitUtils.unpackBits(Long.parseLong(netCapabilities))) { + + for (int capability : stringToIntArray(netCapabilitiesIntArray)) { builder.addCapability(capability); } - for (int forbiddenCapability : BitUtils.unpackBits( - Long.parseLong(netforbiddenCapabilities))) { + + for (int forbiddenCapability : stringToIntArray(netForbiddenCapabilitiesIntArray)) { builder.addForbiddenCapability(forbiddenCapability); } - for (int transport : BitUtils.unpackBits(Long.parseLong(netTransportTypes))) { + + for (int transport : stringToIntArray(netTransportTypesIntArray)) { builder.addTransportType(transport); } jobBuilder.setRequiredNetwork(builder.build()); + } else if (netCapabilitiesLong != null && netTransportTypesLong != null) { + final NetworkRequest.Builder builder = new NetworkRequest.Builder() + .clearCapabilities(); + final int maxNetCapabilityInR = NET_CAPABILITY_TEMPORARILY_NOT_METERED; + // We're okay throwing NFE here; caught by caller + for (int capability : BitUtils.unpackBits(Long.parseLong( + netCapabilitiesLong))) { + if (capability <= maxNetCapabilityInR) { + builder.addCapability(capability); + } + } + for (int forbiddenCapability : BitUtils.unpackBits(Long.parseLong( + netForbiddenCapabilitiesLong))) { + if (forbiddenCapability <= maxNetCapabilityInR) { + builder.addForbiddenCapability(forbiddenCapability); + } + } + + final int maxTransportInR = TRANSPORT_TEST; + for (int transport : BitUtils.unpackBits(Long.parseLong( + netTransportTypesLong))) { + if (transport <= maxTransportInR) { + builder.addTransportType(transport); + } + } + jobBuilder.setRequiredNetwork(builder.build()); } else { // Read legacy values val = parser.getAttributeValue(null, "connectivity"); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java index 37b3c0431455..d532e20a0158 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java @@ -20,6 +20,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT; import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER; import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK; import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED; +import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; import static android.app.usage.UsageStatsManager.REASON_SUB_MASK; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION; @@ -259,8 +260,10 @@ public class AppIdleHistory { int bucketingReason = REASON_MAIN_USAGE | usageReason; final boolean isUserUsage = isUserUsage(bucketingReason); - if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage) { - // Only user usage should bring an app out of the RESTRICTED bucket. + if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage + && (appUsageHistory.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) { + // Only user usage should bring an app out of the RESTRICTED bucket, unless the app + // just timed out into RESTRICTED. newBucket = STANDBY_BUCKET_RESTRICTED; bucketingReason = appUsageHistory.bucketingReason; } else { diff --git a/api/Android.bp b/api/Android.bp index db1f64c57e2c..2ea180ebf598 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -24,6 +24,41 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +python_binary_host { + name: "api_versions_trimmer", + srcs: ["api_versions_trimmer.py"], + version: { + py2: { + enabled: false, + }, + py3: { + enabled: true, + embedded_launcher: false, + }, + }, +} + +python_test_host { + name: "api_versions_trimmer_unittests", + main: "api_versions_trimmer_unittests.py", + srcs: [ + "api_versions_trimmer_unittests.py", + "api_versions_trimmer.py", + ], + test_options: { + unit_test: true, + }, + version: { + py2: { + enabled: false, + }, + py3: { + enabled: true, + embedded_launcher: false, + }, + }, +} + metalava_cmd = "$(location metalava)" // Silence reflection warnings. See b/168689341 metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED " @@ -69,7 +104,10 @@ genrule { dest: "current.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/public/api", dest: "android.txt", }, @@ -151,7 +189,10 @@ genrule { dest: "removed.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/public/api", dest: "removed.txt", }, @@ -187,7 +228,10 @@ genrule { dest: "system-current.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/system/api", dest: "android.txt", }, @@ -242,7 +286,10 @@ genrule { dest: "system-removed.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/system/api", dest: "removed.txt", }, @@ -279,7 +326,10 @@ genrule { dest: "module-lib-current.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/module-lib/api", dest: "android.txt", }, @@ -336,7 +386,10 @@ genrule { dest: "module-lib-removed.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/module-lib/api", dest: "removed.txt", }, @@ -377,7 +430,10 @@ genrule { dest: "system-server-current.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/system-server/api", dest: "android.txt", }, @@ -401,9 +457,50 @@ genrule { dest: "system-server-removed.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/system-server/api", dest: "removed.txt", }, ], } + +// This rule will filter classes present in the jar files of mainline modules +// from the lint database in api-versions.xml. +// This is done to reduce the number of false positive NewApi findings in +// java libraries that compile against the module SDK +genrule { + name: "api-versions-xml-public-filtered", + srcs: [ + // Note: order matters: first parameter is the full api-versions.xml + // after that the stubs files in any order + // stubs files are all modules that export API surfaces EXCEPT ART + ":framework-doc-stubs{.api_versions.xml}", + ":android.net.ipsec.ike.stubs{.jar}", + ":conscrypt.module.public.api.stubs{.jar}", + ":framework-appsearch.stubs{.jar}", + ":framework-connectivity.stubs{.jar}", + ":framework-graphics.stubs{.jar}", + ":framework-media.stubs{.jar}", + ":framework-mediaprovider.stubs{.jar}", + ":framework-permission.stubs{.jar}", + ":framework-permission-s.stubs{.jar}", + ":framework-scheduling.stubs{.jar}", + ":framework-sdkextensions.stubs{.jar}", + ":framework-statsd.stubs{.jar}", + ":framework-tethering.stubs{.jar}", + ":framework-wifi.stubs{.jar}", + ":i18n.module.public.api.stubs{.jar}", + ], + out: ["api-versions-public-filtered.xml"], + tools: ["api_versions_trimmer"], + cmd: "$(location api_versions_trimmer) $(out) $(in)", + dist: { + targets: [ + "sdk", + "win_sdk", + ], + }, +} diff --git a/api/OWNERS b/api/OWNERS index 88d0b61a2ab6..a0272709ffc0 100644 --- a/api/OWNERS +++ b/api/OWNERS @@ -1 +1,6 @@ +hansson@google.com + +# Modularization team +file:platform/packages/modules/common:/OWNERS + per-file Android.bp = file:platform/build/soong:/OWNERS diff --git a/api/api_versions_trimmer.py b/api/api_versions_trimmer.py new file mode 100755 index 000000000000..9afd95a3003a --- /dev/null +++ b/api/api_versions_trimmer.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Script to remove mainline APIs from the api-versions.xml.""" + +import argparse +import re +import xml.etree.ElementTree as ET +import zipfile + + +def read_classes(stubs): + """Read classes from the stubs file. + + Args: + stubs: argument can be a path to a file (a string), a file-like object or a + path-like object + + Returns: + a set of the classes found in the file (set of strings) + """ + classes = set() + with zipfile.ZipFile(stubs) as z: + for info in z.infolist(): + if (not info.is_dir() + and info.filename.endswith(".class") + and not info.filename.startswith("META-INF")): + # drop ".class" extension + classes.add(info.filename[:-6]) + return classes + + +def filter_method_tag(method, classes_to_remove): + """Updates the signature of this method by calling filter_method_signature. + + Updates the method passed into this function. + + Args: + method: xml element that represents a method + classes_to_remove: set of classes you to remove + """ + filtered = filter_method_signature(method.get("name"), classes_to_remove) + method.set("name", filtered) + + +def filter_method_signature(signature, classes_to_remove): + """Removes mentions of certain classes from this method signature. + + Replaces any existing classes that need to be removed, with java/lang/Object + + Args: + signature: string that is a java representation of a method signature + classes_to_remove: set of classes you to remove + """ + regex = re.compile("L.*?;") + start = signature.find("(") + matches = set(regex.findall(signature[start:])) + for m in matches: + # m[1:-1] to drop the leading `L` and `;` ending + if m[1:-1] in classes_to_remove: + signature = signature.replace(m, "Ljava/lang/Object;") + return signature + + +def filter_lint_database(database, classes_to_remove, output): + """Reads a lint database and writes a filtered version without some classes. + + Reads database from api-versions.xml and removes any references to classes + in the second argument. Writes the result (another xml with the same format + of the database) to output. + + Args: + database: path to xml with lint database to read + classes_to_remove: iterable (ideally a set or similar for quick + lookups) that enumerates the classes that should be removed + output: path to write the filtered database + """ + xml = ET.parse(database) + root = xml.getroot() + for c in xml.findall("class"): + cname = c.get("name") + if cname in classes_to_remove: + root.remove(c) + else: + # find the <extends /> tag inside this class to see if the parent + # has been removed from the known classes (attribute called name) + super_classes = c.findall("extends") + for super_class in super_classes: + super_class_name = super_class.get("name") + if super_class_name in classes_to_remove: + super_class.set("name", "java/lang/Object") + interfaces = c.findall("implements") + for interface in interfaces: + interface_name = interface.get("name") + if interface_name in classes_to_remove: + c.remove(interface) + for method in c.findall("method"): + filter_method_tag(method, classes_to_remove) + xml.write(output) + + +def main(): + """Run the program.""" + parser = argparse.ArgumentParser( + description= + ("Read a lint database (api-versions.xml) and many stubs jar files. " + "Produce another database file that doesn't include the classes present " + "in the stubs file(s).")) + parser.add_argument("output", help="Destination of the result (xml file).") + parser.add_argument( + "api_versions", + help="The lint database (api-versions.xml file) to read data from" + ) + parser.add_argument("stubs", nargs="+", help="The stubs jar file(s)") + parsed = parser.parse_args() + classes = set() + for stub in parsed.stubs: + classes.update(read_classes(stub)) + filter_lint_database(parsed.api_versions, classes, parsed.output) + + +if __name__ == "__main__": + main() diff --git a/api/api_versions_trimmer_unittests.py b/api/api_versions_trimmer_unittests.py new file mode 100644 index 000000000000..4eb929ea1b5d --- /dev/null +++ b/api/api_versions_trimmer_unittests.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import io +import re +import unittest +import xml.etree.ElementTree as ET +import zipfile + +import api_versions_trimmer + + +def create_in_memory_zip_file(files): + f = io.BytesIO() + with zipfile.ZipFile(f, "w") as z: + for fname in files: + with z.open(fname, mode="w") as class_file: + class_file.write(b"") + return f + + +def indent(elem, level=0): + i = "\n" + level * " " + j = "\n" + (level - 1) * " " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for subelem in elem: + indent(subelem, level + 1) + if not elem.tail or not elem.tail.strip(): + elem.tail = j + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = j + return elem + + +def pretty_print(s): + tree = ET.parse(io.StringIO(s)) + el = indent(tree.getroot()) + res = ET.tostring(el).decode("utf-8") + # remove empty lines inside the result because this still breaks some + # comparisons + return re.sub(r"\n\s*\n", "\n", res, re.MULTILINE) + + +class ApiVersionsTrimmerUnittests(unittest.TestCase): + + def setUp(self): + # so it prints diffs in long strings (xml files) + self.maxDiff = None + + def test_read_classes(self): + f = create_in_memory_zip_file( + ["a/b/C.class", + "a/b/D.class", + ] + ) + res = api_versions_trimmer.read_classes(f) + self.assertEqual({"a/b/C", "a/b/D"}, res) + + def test_read_classes_ignore_dex(self): + f = create_in_memory_zip_file( + ["a/b/C.class", + "a/b/D.class", + "a/b/E.dex", + "f.dex", + ] + ) + res = api_versions_trimmer.read_classes(f) + self.assertEqual({"a/b/C", "a/b/D"}, res) + + def test_read_classes_ignore_manifest(self): + f = create_in_memory_zip_file( + ["a/b/C.class", + "a/b/D.class", + "META-INFO/G.class" + ] + ) + res = api_versions_trimmer.read_classes(f) + self.assertEqual({"a/b/C", "a/b/D"}, res) + + def test_filter_method_signature(self): + xml = """ + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/> + """ + method = ET.fromstring(xml) + classes_to_remove = {"android/accessibilityservice/GestureDescription"} + expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" + api_versions_trimmer.filter_method_tag(method, classes_to_remove) + self.assertEqual(expected, method.get("name")) + + def test_filter_method_signature_with_L_in_method(self): + xml = """ + <method name="dispatchLeftGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/> + """ + method = ET.fromstring(xml) + classes_to_remove = {"android/accessibilityservice/GestureDescription"} + expected = "dispatchLeftGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" + api_versions_trimmer.filter_method_tag(method, classes_to_remove) + self.assertEqual(expected, method.get("name")) + + def test_filter_method_signature_with_L_in_class(self): + xml = """ + <method name="dispatchGesture(Landroid/accessibilityservice/LeftGestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/> + """ + method = ET.fromstring(xml) + classes_to_remove = {"android/accessibilityservice/LeftGestureDescription"} + expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" + api_versions_trimmer.filter_method_tag(method, classes_to_remove) + self.assertEqual(expected, method.get("name")) + + def test_filter_method_signature_with_inner_class(self): + xml = """ + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription$Inner;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/> + """ + method = ET.fromstring(xml) + classes_to_remove = {"android/accessibilityservice/GestureDescription$Inner"} + expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" + api_versions_trimmer.filter_method_tag(method, classes_to_remove) + self.assertEqual(expected, method.get("name")) + + def _run_filter_db_test(self, database_str, expected): + """Performs the pattern of testing the filter_lint_database method. + + Filters instances of the class "a/b/C" (hard-coded) from the database string + and compares the result with the expected result (performs formatting of + the xml of both inputs) + + Args: + database_str: string, the contents of the lint database (api-versions.xml) + expected: string, the expected result after filtering the original + database + """ + database = io.StringIO(database_str) + classes_to_remove = {"a/b/C"} + output = io.BytesIO() + api_versions_trimmer.filter_lint_database( + database, + classes_to_remove, + output + ) + expected = pretty_print(expected) + res = pretty_print(output.getvalue().decode("utf-8")) + self.assertEqual(expected, res) + + def test_filter_lint_database_updates_method_signature_params(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/E" since="1"> + <!-- extends will be modified --> + <extends name="a/b/C"/> + <!-- first parameter will be modified --> + <method name="dispatchGesture(La/b/C;Landroid/os/Handler;)Z" since="24"/> + <!-- second should remain untouched --> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + <class name="a/b/E" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """) + + def test_filter_lint_database_updates_method_signature_return(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/E" since="1"> + <!-- extends will be modified --> + <extends name="a/b/C"/> + <!-- return type should be changed --> + <method name="gestureIdToString(I)La/b/C;" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + <class name="a/b/E" since="1"> + + <extends name="java/lang/Object"/> + + <method name="gestureIdToString(I)Ljava/lang/Object;" since="24"/> + </class> + </api> + """) + + def test_filter_lint_database_removes_implements(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/D" since="1"> + <extends name="java/lang/Object"/> + <implements name="a/b/C"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + + <class name="a/b/D" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """) + + def test_filter_lint_database_updates_extends(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/E" since="1"> + <!-- extends will be modified --> + <extends name="a/b/C"/> + <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + <class name="a/b/E" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """) + + def test_filter_lint_database_removes_class(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/D" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + + <class name="a/b/D" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """) + + +if __name__ == "__main__": + unittest.main() diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp index 0eff83c99282..a1575173ded6 100644 --- a/cmds/app_process/Android.bp +++ b/cmds/app_process/Android.bp @@ -29,7 +29,16 @@ cc_binary { }, }, - ldflags: ["-Wl,--export-dynamic"], + // Symbols exported from the executable in .dynsym interpose symbols in every + // linker namespace, including an app's classloader namespace. Provide this + // version script to prevent unwanted interposition. + // + // By default, the static linker doesn't export most of an executable's symbols, + // but it will export a symbol that appears to override a symbol in a needed DSO. + // This commonly happens with C++ vaguely-linked entities, such as template + // functions or type_info variables. Hence, a version script is needed even for + // an executable. + version_script: "version-script.txt", shared_libs: [ "libandroid_runtime", diff --git a/cmds/app_process/version-script.txt b/cmds/app_process/version-script.txt new file mode 100644 index 000000000000..a98066a67675 --- /dev/null +++ b/cmds/app_process/version-script.txt @@ -0,0 +1,4 @@ +{ + local: + *; +}; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 0bb12c84fe86..a4ac61b1f086 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1769,6 +1769,7 @@ package android.os { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public String getUserType(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle); @@ -2723,6 +2724,7 @@ package android.view { public final class InputDevice implements android.os.Parcelable { method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void disable(); method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void enable(); + field public static final int ACCESSIBILITY_DEVICE_ID = -2; // 0xfffffffe } public class KeyEvent extends android.view.InputEvent implements android.os.Parcelable { @@ -3189,6 +3191,7 @@ package android.window { method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public android.window.WindowContainerToken getImeTarget(int); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getRootTasks(int, @NonNull int[]); + method @BinderThread public void onAppSplashScreenViewRemoved(int); method @BinderThread public void onBackPressedOnTaskRoot(@NonNull android.app.ActivityManager.RunningTaskInfo); method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl); method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 1ce598b5fa18..8e1f263ebf03 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -419,7 +419,7 @@ public class ActivityOptions { private IBinder mLaunchCookie; private IRemoteTransition mRemoteTransition; private boolean mOverrideTaskTransition; - private int mSplashScreenThemeResId; + private String mSplashScreenThemeResName; @SplashScreen.SplashScreenStyle private int mSplashScreenStyle; private boolean mRemoveWithTaskOrganizer; @@ -1174,7 +1174,7 @@ public class ActivityOptions { mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder( KEY_REMOTE_TRANSITION)); mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION); - mSplashScreenThemeResId = opts.getInt(KEY_SPLASH_SCREEN_THEME); + mSplashScreenThemeResName = opts.getString(KEY_SPLASH_SCREEN_THEME); mRemoveWithTaskOrganizer = opts.getBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER); mLaunchedFromBubble = opts.getBoolean(KEY_LAUNCHED_FROM_BUBBLE); mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH); @@ -1368,8 +1368,9 @@ public class ActivityOptions { * Gets whether the activity want to be launched as other theme for the splash screen. * @hide */ - public int getSplashScreenThemeResId() { - return mSplashScreenThemeResId; + @Nullable + public String getSplashScreenThemeResName() { + return mSplashScreenThemeResName; } /** @@ -1945,8 +1946,8 @@ public class ActivityOptions { if (mOverrideTaskTransition) { b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition); } - if (mSplashScreenThemeResId != 0) { - b.putInt(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResId); + if (mSplashScreenThemeResName != null && !mSplashScreenThemeResName.isEmpty()) { + b.putString(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResName); } if (mRemoveWithTaskOrganizer) { b.putBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER, mRemoveWithTaskOrganizer); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 02ab3143eed9..034ad8e83fd3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -447,7 +447,7 @@ public final class ActivityThread extends ClientTransactionHandler @GuardedBy("mLock") ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter - Object mLock; // The lock to be used to get notified when the provider is ready + final Object mLock; // The lock to be used to get notified when the provider is ready public ProviderKey(String authority, int userId) { this.authority = authority; @@ -1827,11 +1827,14 @@ public final class ActivityThread extends ClientTransactionHandler @Override public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder, - @NonNull String auth, int userId, boolean published) { - final ProviderKey key = getGetProviderKey(auth, userId); - synchronized (key.mLock) { - key.mHolder = holder; - key.mLock.notifyAll(); + @NonNull String authorities, int userId, boolean published) { + final String auths[] = authorities.split(";"); + for (String auth: auths) { + final ProviderKey key = getGetProviderKey(auth, userId); + synchronized (key.mLock) { + key.mHolder = holder; + key.mLock.notifyAll(); + } } } diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index c3272c1a770d..7a806bdf473d 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -45,6 +45,7 @@ public final class AutomaticZenRule implements Parcelable { private long creationTime; private ZenPolicy mZenPolicy; private boolean mModified = false; + private String mPkg; /** * Creates an automatic zen rule. @@ -123,6 +124,7 @@ public final class AutomaticZenRule implements Parcelable { creationTime = source.readLong(); mZenPolicy = source.readParcelable(null); mModified = source.readInt() == ENABLED; + mPkg = source.readString(); } /** @@ -244,6 +246,20 @@ public final class AutomaticZenRule implements Parcelable { this.configurationActivity = componentName; } + /** + * @hide + */ + public void setPackageName(String pkgName) { + mPkg = pkgName; + } + + /** + * @hide + */ + public String getPackageName() { + return mPkg; + } + @Override public int describeContents() { return 0; @@ -265,6 +281,7 @@ public final class AutomaticZenRule implements Parcelable { dest.writeLong(creationTime); dest.writeParcelable(mZenPolicy, 0); dest.writeInt(mModified ? ENABLED : DISABLED); + dest.writeString(mPkg); } @Override @@ -273,6 +290,7 @@ public final class AutomaticZenRule implements Parcelable { .append("enabled=").append(enabled) .append(",name=").append(name) .append(",interruptionFilter=").append(interruptionFilter) + .append(",pkg=").append(mPkg) .append(",conditionId=").append(conditionId) .append(",owner=").append(owner) .append(",configActivity=").append(configurationActivity) @@ -294,13 +312,14 @@ public final class AutomaticZenRule implements Parcelable { && Objects.equals(other.owner, owner) && Objects.equals(other.mZenPolicy, mZenPolicy) && Objects.equals(other.configurationActivity, configurationActivity) + && Objects.equals(other.mPkg, mPkg) && other.creationTime == creationTime; } @Override public int hashCode() { return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, - configurationActivity, mZenPolicy, mModified, creationTime); + configurationActivity, mZenPolicy, mModified, creationTime, mPkg); } public static final @android.annotation.NonNull Parcelable.Creator<AutomaticZenRule> CREATOR diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 4555c1725a56..d6ff6d3dfc3a 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -153,7 +153,7 @@ oneway interface IApplicationThread { void performDirectAction(IBinder activityToken, String actionId, in Bundle arguments, in RemoteCallback cancellationCallback, in RemoteCallback resultCallback); - void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth, + void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String authorities, int userId, boolean published); void instrumentWithoutRestart(in ComponentName instrumentationName, in Bundle instrumentationArgs, diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index f33adb3c01d1..098492c8234b 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -207,7 +207,7 @@ interface INotificationManager void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted); AutomaticZenRule getAutomaticZenRule(String id); List<ZenModeConfig.ZenRule> getZenRules(); - String addAutomaticZenRule(in AutomaticZenRule automaticZenRule); + String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg); boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule); boolean removeAutomaticZenRule(String id); boolean removeAutomaticZenRules(String packageName); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index ba0bc555eb57..506dfe09f3fa 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5606,14 +5606,24 @@ public class Notification implements Parcelable final boolean snoozeEnabled = !hideSnoozeButton && mContext.getContentResolver() != null - && (Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1); + && isSnoozeSettingEnabled(); if (snoozeEnabled) { big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target, RemoteViews.MARGIN_BOTTOM, 0); } } + private boolean isSnoozeSettingEnabled() { + try { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1; + } catch (SecurityException ex) { + // Most 3p apps can't access this snooze setting, so their NotificationListeners + // would be unable to create notification views if we propagated this exception. + return false; + } + } + /** * Returns the actions that are not contextual. */ diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index da03a3da820a..ccf1edb3fecc 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1182,10 +1182,12 @@ public class NotificationManager { List<ZenModeConfig.ZenRule> rules = service.getZenRules(); Map<String, AutomaticZenRule> ruleMap = new HashMap<>(); for (ZenModeConfig.ZenRule rule : rules) { - ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component, + AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity, rule.conditionId, rule.zenPolicy, zenModeToInterruptionFilter(rule.zenMode), rule.enabled, - rule.creationTime)); + rule.creationTime); + azr.setPackageName(rule.pkg); + ruleMap.put(rule.id, azr); } return ruleMap; } catch (RemoteException e) { @@ -1226,7 +1228,7 @@ public class NotificationManager { public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) { INotificationManager service = getService(); try { - return service.addAutomaticZenRule(automaticZenRule); + return service.addAutomaticZenRule(automaticZenRule, mContext.getPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index e68eb7471e42..0136a35e3975 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -365,7 +365,6 @@ public final class PendingIntent implements Parcelable { } if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED) - && !"com.google.android.apps.gcs".equals(packageName) && !flagImmutableSet && !flagMutableSet) { String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S + " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE" diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 04a12afb8039..6ca7dfbe2284 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -34,6 +34,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -260,12 +261,19 @@ public abstract class PropertyInvalidatedCache<Query, Result> { private static final HashMap<String, Integer> sCorks = new HashMap<>(); /** + * A map of cache keys that have been disabled in the local process. When a key is + * disabled locally, existing caches are disabled and the key is saved in this map. + * Future cache instances that use the same key will be disabled in their constructor. + */ + @GuardedBy("sCorkLock") + private static final HashSet<String> sDisabledKeys = new HashSet<>(); + + /** * Weakly references all cache objects in the current process, allowing us to iterate over * them all for purposes like issuing debug dumps and reacting to memory pressure. */ @GuardedBy("sCorkLock") - private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = - new WeakHashMap<>(); + private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>(); private final Object mLock = new Object(); @@ -348,6 +356,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { }; synchronized (sCorkLock) { sCaches.put(this, null); + if (sDisabledKeys.contains(mCacheName)) { + disableInstance(); + } } } @@ -372,6 +383,14 @@ public abstract class PropertyInvalidatedCache<Query, Result> { protected abstract Result recompute(Query query); /** + * Return true if the query should bypass the cache. The default behavior is to + * always use the cache but the method can be overridden for a specific class. + */ + protected boolean bypass(Query query) { + return false; + } + + /** * Determines if a pair of responses are considered equal. Used to determine whether * a cache is inadvertently returning stale results when VERIFY is set to true. */ @@ -414,7 +433,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { /** * Disable the use of this cache in this process. */ - public final void disableLocal() { + public final void disableInstance() { synchronized (mLock) { mDisabled = true; clear(); @@ -422,6 +441,30 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } /** + * Disable the local use of all caches with the same name. All currently registered caches + * using the key will be disabled now, and all future cache instances that use the key will be + * disabled in their constructor. + */ + public static final void disableLocal(@NonNull String name) { + synchronized (sCorkLock) { + sDisabledKeys.add(name); + for (PropertyInvalidatedCache cache : sCaches.keySet()) { + if (name.equals(cache.mCacheName)) { + cache.disableInstance(); + } + } + } + } + + /** + * Disable this cache in the current process, and all other caches that use the same + * property. + */ + public final void disableLocal() { + disableLocal(mCacheName); + } + + /** * Return whether the cache is disabled in this process. */ public final boolean isDisabledLocal() { @@ -435,8 +478,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> { // Let access to mDisabled race: it's atomic anyway. long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED; for (;;) { - if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET || - currentNonce == NONCE_CORKED) { + if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET + || currentNonce == NONCE_CORKED || bypass(query)) { if (!mDisabled) { // Do not bother collecting statistics if the cache is // locally disabled. @@ -875,6 +918,15 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } /** + * Report the disabled status of this cache instance. The return value does not + * reflect status of the property key. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public boolean getDisabledState() { + return isDisabledLocal(); + } + + /** * Returns a list of caches alive at the current time. */ public static ArrayList<PropertyInvalidatedCache> getActiveCaches() { diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 0ab3f2f4be46..7cb1d89aa954 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -697,6 +697,18 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * service element of manifest file. The value of attribute * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p> * + * <div class="caution"> + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> + * Behavior changes: Apps targeting Android 12 + * </a> + * for more details. + * </div> + * * @throws ForegroundServiceStartNotAllowedException * If the app targeting API is * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from @@ -733,6 +745,18 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST} to use all flags that * is specified in manifest attribute foregroundServiceType.</p> * + * <div class="caution"> + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> + * Behavior changes: Apps targeting Android 12 + * </a> + * for more details. + * </div> + * * @param id The identifier for this notification as per * {@link NotificationManager#notify(int, Notification) * NotificationManager.notify(int, Notification)}; must not be 0. diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index cd82deb13e9b..2777a7aae517 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -111,12 +111,15 @@ public final class WallpaperColors implements Parcelable { public WallpaperColors(Parcel parcel) { mMainColors = new ArrayList<>(); mAllColors = new HashMap<>(); - final int count = parcel.readInt(); + int count = parcel.readInt(); for (int i = 0; i < count; i++) { final int colorInt = parcel.readInt(); Color color = Color.valueOf(colorInt); mMainColors.add(color); - + } + count = parcel.readInt(); + for (int i = 0; i < count; i++) { + final int colorInt = parcel.readInt(); final int population = parcel.readInt(); mAllColors.put(colorInt, population); } @@ -411,9 +414,16 @@ public final class WallpaperColors implements Parcelable { for (int i = 0; i < count; i++) { Color color = mainColors.get(i); dest.writeInt(color.toArgb()); - Integer population = mAllColors.get(color.toArgb()); - int populationInt = (population != null) ? population : 0; - dest.writeInt(populationInt); + } + count = mAllColors.size(); + dest.writeInt(count); + for (Map.Entry<Integer, Integer> colorEntry : mAllColors.entrySet()) { + if (colorEntry.getKey() != null) { + dest.writeInt(colorEntry.getKey()); + Integer population = mAllColors.get(colorEntry.getValue()); + int populationInt = (population != null) ? population : 0; + dest.writeInt(populationInt); + } } dest.writeInt(mColorHints); } @@ -476,12 +486,13 @@ public final class WallpaperColors implements Parcelable { WallpaperColors other = (WallpaperColors) o; return mMainColors.equals(other.mMainColors) + && mAllColors.equals(other.mAllColors) && mColorHints == other.mColorHints; } @Override public int hashCode() { - return 31 * mMainColors.hashCode() + mColorHints; + return (31 * mMainColors.hashCode() * mAllColors.hashCode()) + mColorHints; } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 52c58e162289..8284cdde3d06 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -11871,7 +11871,19 @@ public class DevicePolicyManager { public boolean isAffiliatedUser() { throwIfParentInstance("isAffiliatedUser"); try { - return mService.isAffiliatedUser(); + return mService.isCallingUserAffiliated(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Returns whether target user is affiliated with the device. + */ + public boolean isAffiliatedUser(@UserIdInt int userId) { + try { + return mService.isAffiliatedUser(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -13884,7 +13896,11 @@ public class DevicePolicyManager { } /** - * Returns whether USB data signaling is currently enabled by the admin. Callable by any app. + * Returns whether USB data signaling is currently enabled. + * + * <p> When called by a device owner or profile owner of an organization-owned managed profile, + * this API returns whether USB data signaling is currently enabled by that admin. When called + * by any other app, returns whether USB data signaling is currently enabled on the device. * * @return {@code true} if USB data signaling is enabled, {@code false} otherwise. */ diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index a9bec98ce405..a0d2977cf09a 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -18,7 +18,6 @@ package android.app.admin; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.app.admin.DevicePolicyManager.OperationSafetyReason; import android.content.ComponentName; import android.content.Intent; import android.os.UserHandle; @@ -256,13 +255,4 @@ public abstract class DevicePolicyManagerInternal { * {@link #supportsResetOp(int)} is true. */ public abstract void resetOp(int op, String packageName, @UserIdInt int userId); - - /** - * Notifies the system that an unsafe operation reason has changed. - * - * @throws IllegalArgumentException if {@code checker} is not the same as set on - * {@code DevicePolicyManagerService}. - */ - public abstract void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker, - @OperationSafetyReason int reason, boolean isSafe); } diff --git a/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java b/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java new file mode 100644 index 000000000000..ccb99470d372 --- /dev/null +++ b/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.admin; + +import android.app.admin.DevicePolicyManager.OperationSafetyReason; + +/** + * Device policy manager local system service interface for methods that don't require the + * {@code device_admin} feature. + * + * Maintenance note: if you need to expose information from DPMS to lower level services such as + * PM/UM/AM/etc, then exposing it from DevicePolicyManagerInternal is not safe because it may cause + * lock order inversion. Consider using {@link DevicePolicyCache} instead. + * + * @hide Only for use within the system server. + */ +public interface DevicePolicyManagerLiteInternal { + + /** + * Notifies the system that an unsafe operation reason has changed. + * + * @throws IllegalArgumentException if {@code checker} is not the same as set on + * {@code DevicePolicyManagerService.setDevicePolicySafetyChecker()}. + */ + void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker, + @OperationSafetyReason int reason, boolean isSafe); +} diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index db2fc0d19b4c..b6c48a1c057b 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -390,7 +390,8 @@ interface IDevicePolicyManager { void setAffiliationIds(in ComponentName admin, in List<String> ids); List<String> getAffiliationIds(in ComponentName admin); - boolean isAffiliatedUser(); + boolean isCallingUserAffiliated(); + boolean isAffiliatedUser(int userId); void setSecurityLoggingEnabled(in ComponentName admin, String packageName, boolean enabled); boolean isSecurityLoggingEnabled(in ComponentName admin, String packageName); diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS index 3523ee0640ab..2239100a5523 100644 --- a/core/java/android/bluetooth/OWNERS +++ b/core/java/android/bluetooth/OWNERS @@ -2,3 +2,4 @@ zachoverflow@google.com siyuanh@google.com +rahulsabnis@google.com diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ea0e321377c8..9c60f431b06e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3182,7 +3182,8 @@ public abstract class Context { * <p>This function will throw {@link SecurityException} if you do not * have permission to start the given service. * - * <p class="note"><strong>Note:</strong> Each call to startService() + * <div class="caution"> + * <p><strong>Note:</strong> Each call to startService() * results in significant work done by the system to manage service * lifecycle surrounding the processing of the intent, which can take * multiple milliseconds of CPU time. Due to this cost, startService() @@ -3191,6 +3192,25 @@ public abstract class Context { * for high frequency calls. * </p> * + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#O}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#O} + * or higher are not allowed to start background services from the background. + * See + * <a href="{@docRoot}/about/versions/oreo/background"> + * Background Execution Limits</a> + * for more details. + * + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> + * Behavior changes: Apps targeting Android 12 + * </a> + * for more details. + * </div> + * * @param service Identifies the service to be started. The Intent must be * fully explicit (supplying a component name). Additional values * may be included in the Intent extras to supply arguments along with @@ -3215,6 +3235,7 @@ public abstract class Context { * This excemption extends {@link IllegalStateException}, so apps can * use {@code catch (IllegalStateException)} to catch both. * + * @see #startForegroundService(Intent) * @see #stopService * @see #bindService */ @@ -3232,6 +3253,18 @@ public abstract class Context { * at any time, regardless of whether the app hosting the service is in a foreground * state. * + * <div class="caution"> + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> + * Behavior changes: Apps targeting Android 12 + * </a> + * for more details. + * </div> + * * @param service Identifies the service to be started. The Intent must be * fully explicit (supplying a component name). Additional values * may be included in the Intent extras to supply arguments along with diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index af5f9ceb14d8..60ab83aa2264 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -524,8 +524,12 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * owned ActivityContainer such as that within an ActivityView. If not set and * this activity is launched into such a container a SecurityException will be * thrown. Set from the {@link android.R.attr#allowEmbedded} attribute. + * + * @deprecated this flag is no longer needed since ActivityView is now fully removed + * TODO(b/191165536): delete this flag since is no longer used */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Deprecated public static final int FLAG_ALLOW_EMBEDDED = 0x80000000; /** diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java index 63f93bfa24e5..806091e2158d 100644 --- a/core/java/android/content/pm/AppSearchShortcutInfo.java +++ b/core/java/android/content/pm/AppSearchShortcutInfo.java @@ -423,7 +423,7 @@ public class AppSearchShortcutInfo extends GenericDocument { shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage, disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras, getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri, - disabledReason, persons, locusId, 0); + disabledReason, persons, locusId, null); si.setImplicitRank(implicitRank); if ((implicitRank & ShortcutInfo.RANK_CHANGED_BIT) != 0) { si.setRankChanged(); diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index 32fc74f60d15..12911d6e1232 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -62,5 +62,8 @@ interface IPackageInstaller { void bypassNextStagedInstallerCheck(boolean value); + void bypassNextAllowedApexUpdateCheck(boolean value); + void setAllowUnlimitedSilentUpdates(String installerPackageName); + void setSilentUpdatesThrottleTime(long throttleTimeInSeconds); } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index c2ac80e7c98f..d3ed00608324 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -586,11 +586,6 @@ interface IPackageManager { String targetCompilerFilter, boolean force); /** - * Ask the package manager to compile layouts in the given package. - */ - boolean compileLayouts(String packageName); - - /** * Ask the package manager to dump profiles associated with a package. */ void dumpProfiles(String packageName); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 33a34be1b968..2ed00b5d2982 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1278,6 +1278,13 @@ public abstract class PackageManager { */ public static final int INSTALL_STAGED = 0x00200000; + /** + * Flag parameter for {@link #installPackage} to indicate that check whether given APEX can be + * updated should be disabled for this install. + * @hide + */ + public static final int INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK = 0x00400000; + /** @hide */ @IntDef(flag = true, value = { DONT_KILL_APP, diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java index 52ee4de5bed6..d1577684aac6 100644 --- a/core/java/android/content/pm/PackagePartitions.java +++ b/core/java/android/content/pm/PackagePartitions.java @@ -119,6 +119,9 @@ public class PackagePartitions { @Nullable private final DeferredCanonicalFile mOverlayFolder; + @NonNull + private final File mNonConicalFolder; + private SystemPartition(@NonNull File folder, @PartitionType int type, boolean containsPrivApp, boolean containsOverlay) { this.type = type; @@ -128,6 +131,7 @@ public class PackagePartitions { : null; this.mOverlayFolder = containsOverlay ? new DeferredCanonicalFile(folder, "overlay") : null; + this.mNonConicalFolder = folder; } public SystemPartition(@NonNull SystemPartition original) { @@ -136,6 +140,7 @@ public class PackagePartitions { this.mAppFolder = original.mAppFolder; this.mPrivAppFolder = original.mPrivAppFolder; this.mOverlayFolder = original.mOverlayFolder; + this.mNonConicalFolder = original.mNonConicalFolder; } /** @@ -153,6 +158,12 @@ public class PackagePartitions { return mFolder.getFile(); } + /** Returns the non-canonical folder of the partition. */ + @NonNull + public File getNonConicalFolder() { + return mNonConicalFolder; + } + /** Returns the canonical app folder of the partition. */ @Nullable public File getAppFolder() { diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 76712b5ce2dc..a264bebb5d88 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -449,7 +449,7 @@ public final class ShortcutInfo implements Parcelable { private int mDisabledReason; - private int mStartingThemeResId; + @Nullable private String mStartingThemeResName; private ShortcutInfo(Builder b) { mUserId = b.mContext.getUserId(); @@ -478,8 +478,9 @@ public final class ShortcutInfo implements Parcelable { mExtras = b.mExtras; mLocusId = b.mLocusId; + mStartingThemeResName = b.mStartingThemeResId != 0 + ? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null; updateTimestamp(); - mStartingThemeResId = b.mStartingThemeResId; } /** @@ -626,7 +627,7 @@ public final class ShortcutInfo implements Parcelable { // Set this bit. mFlags |= FLAG_KEY_FIELDS_ONLY; } - mStartingThemeResId = source.mStartingThemeResId; + mStartingThemeResName = source.mStartingThemeResName; } /** @@ -950,8 +951,8 @@ public final class ShortcutInfo implements Parcelable { if (source.mLocusId != null) { mLocusId = source.mLocusId; } - if (source.mStartingThemeResId != 0) { - mStartingThemeResId = source.mStartingThemeResId; + if (source.mStartingThemeResName != null && !source.mStartingThemeResName.isEmpty()) { + mStartingThemeResName = source.mStartingThemeResName; } } @@ -1454,11 +1455,12 @@ public final class ShortcutInfo implements Parcelable { } /** - * Returns the theme resource id used for the splash screen. + * Returns the theme resource name used for the splash screen. * @hide */ - public int getStartingThemeResId() { - return mStartingThemeResId; + @Nullable + public String getStartingThemeResName() { + return mStartingThemeResName; } /** @hide -- old signature, the internal code still uses it. */ @@ -2182,7 +2184,7 @@ public final class ShortcutInfo implements Parcelable { mPersons = source.readParcelableArray(cl, Person.class); mLocusId = source.readParcelable(cl); mIconUri = source.readString8(); - mStartingThemeResId = source.readInt(); + mStartingThemeResName = source.readString8(); } @Override @@ -2234,7 +2236,7 @@ public final class ShortcutInfo implements Parcelable { dest.writeParcelableArray(mPersons, flags); dest.writeParcelable(mLocusId, flags); dest.writeString8(mIconUri); - dest.writeInt(mStartingThemeResId); + dest.writeString8(mStartingThemeResName); } public static final @NonNull Creator<ShortcutInfo> CREATOR = @@ -2391,10 +2393,10 @@ public final class ShortcutInfo implements Parcelable { sb.append("disabledReason="); sb.append(getDisabledReasonDebugString(mDisabledReason)); - if (mStartingThemeResId != 0) { + if (mStartingThemeResName != null && !mStartingThemeResName.isEmpty()) { addIndentOrComma(sb, indent); - sb.append("SplashScreenThemeResId="); - sb.append(Integer.toHexString(mStartingThemeResId)); + sb.append("SplashScreenThemeResName="); + sb.append(mStartingThemeResName); } addIndentOrComma(sb, indent); @@ -2482,7 +2484,8 @@ public final class ShortcutInfo implements Parcelable { Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras, long lastChangedTimestamp, int flags, int iconResId, String iconResName, String bitmapPath, String iconUri, - int disabledReason, Person[] persons, LocusId locusId, int startingThemeResId) { + int disabledReason, Person[] persons, LocusId locusId, + @Nullable String startingThemeResName) { mUserId = userId; mId = id; mPackageName = packageName; @@ -2511,6 +2514,6 @@ public final class ShortcutInfo implements Parcelable { mDisabledReason = disabledReason; mPersons = persons; mLocusId = locusId; - mStartingThemeResId = startingThemeResId; + mStartingThemeResName = startingThemeResName; } } diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index 233abf36131b..3ed5c6457fa5 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -74,7 +74,7 @@ public abstract class ShortcutServiceInternal { /** * Get the theme res ID of the starting window, it can be 0 if not specified. */ - public abstract int getShortcutStartingThemeResId(int launcherUserId, + public abstract @Nullable String getShortcutStartingThemeResName(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId); diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index 1e650a807cec..154d9234d00c 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -493,6 +493,7 @@ public class ApkLiteParseUtils { if (targetResult.isError()) { return input.error(targetResult); } + targetSdkVersion = targetResult.getResult(); ParseResult<Integer> minResult = ParsingPackageUtils.computeMinSdkVersion( minVer, minCode, ParsingPackageUtils.SDK_VERSION, @@ -500,8 +501,6 @@ public class ApkLiteParseUtils { if (minResult.isError()) { return input.error(minResult); } - - targetSdkVersion = targetResult.getResult(); minSdkVersion = minResult.getResult(); } } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index ec6c23384859..37469e916eef 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -952,7 +952,8 @@ public final class Sensor { } /** - * @return name string of the sensor. + * @return name string of the sensor. The name is guaranteed to be unique + * for a particular sensor type. */ public String getName() { return mName; diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 0a76a9c6bee3..713b66abed21 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -399,7 +399,9 @@ public abstract class SensorManager { * Use this method to get the list of available sensors of a certain type. * Make multiple calls to get sensors of different types or use * {@link android.hardware.Sensor#TYPE_ALL Sensor.TYPE_ALL} to get all the - * sensors. + * sensors. Note that the {@link android.hardware.Sensor#getName()} is + * expected to yield a value that is unique across any sensors that return + * the same value for {@link android.hardware.Sensor#getType()}. * * <p class="note"> * NOTE: Both wake-up and non wake-up sensors matching the given type are diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java index a0027077e1a3..60a365835e2e 100644 --- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java +++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java @@ -63,6 +63,11 @@ public interface BiometricAuthenticator { */ int TYPE_FACE = 1 << 3; + /** + * @hide + */ + int TYPE_ANY_BIOMETRIC = TYPE_FINGERPRINT | TYPE_IRIS | TYPE_FACE; + @IntDef(flag = true, value = { TYPE_NONE, TYPE_CREDENTIAL, diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index 79f716cd5ce9..2d46a4073809 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -60,7 +60,8 @@ public interface BiometricFingerprintConstants { BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, BIOMETRIC_ERROR_RE_ENROLL, BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, - FINGERPRINT_ERROR_UNKNOWN}) + FINGERPRINT_ERROR_UNKNOWN, + FINGERPRINT_ERROR_BAD_CALIBARTION}) @Retention(RetentionPolicy.SOURCE) @interface FingerprintError {} @@ -181,6 +182,12 @@ public interface BiometricFingerprintConstants { int FINGERPRINT_ERROR_UNKNOWN = 17; /** + * Error indicating that the fingerprint sensor has bad calibration. + * @hide + */ + int FINGERPRINT_ERROR_BAD_CALIBARTION = 18; + + /** * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) diff --git a/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java new file mode 100644 index 000000000000..4ec6f0d2509e --- /dev/null +++ b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.os.Build; +import android.os.UserHandle; +import android.provider.Settings; + +/** + * "Base" functionality. For settings-specific functionality (which may rely on this base + * functionality), see {@link com.android.settings.biometrics.ParentalControlsUtils} + * @hide + */ +public class ParentalControlsUtilsInternal { + + private static final String TEST_ALWAYS_REQUIRE_CONSENT = + "android.hardware.biometrics.ParentalControlsUtilsInternal.always_require_consent"; + + public static boolean isTestModeEnabled(@NonNull Context context) { + if (Build.IS_USERDEBUG || Build.IS_ENG) { + return Settings.Secure.getInt(context.getContentResolver(), + TEST_ALWAYS_REQUIRE_CONSENT, 0) != 0; + } + return false; + } + + public static boolean parentConsentRequired(@NonNull Context context, + @NonNull DevicePolicyManager dpm, @BiometricAuthenticator.Modality int modality, + @NonNull UserHandle userHandle) { + if (isTestModeEnabled(context)) { + return true; + } + + return parentConsentRequired(dpm, modality, userHandle); + } + + /** + * @return true if parental consent is required in order for biometric sensors to be used. + */ + public static boolean parentConsentRequired(@NonNull DevicePolicyManager dpm, + @BiometricAuthenticator.Modality int modality, @NonNull UserHandle userHandle) { + final ComponentName cn = getSupervisionComponentName(dpm, userHandle); + if (cn == null) { + return false; + } + + final int keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(cn); + final boolean dpmFpDisabled = containsFlag(keyguardDisabledFeatures, + DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT); + final boolean dpmFaceDisabled = containsFlag(keyguardDisabledFeatures, + DevicePolicyManager.KEYGUARD_DISABLE_FACE); + final boolean dpmIrisDisabled = containsFlag(keyguardDisabledFeatures, + DevicePolicyManager.KEYGUARD_DISABLE_IRIS); + + final boolean consentRequired; + if (containsFlag(modality, BiometricAuthenticator.TYPE_FINGERPRINT) && dpmFpDisabled) { + consentRequired = true; + } else if (containsFlag(modality, BiometricAuthenticator.TYPE_FACE) && dpmFaceDisabled) { + consentRequired = true; + } else if (containsFlag(modality, BiometricAuthenticator.TYPE_IRIS) && dpmIrisDisabled) { + consentRequired = true; + } else { + consentRequired = false; + } + + return consentRequired; + } + + @Nullable + public static ComponentName getSupervisionComponentName(@NonNull DevicePolicyManager dpm, + @NonNull UserHandle userHandle) { + return dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle); + } + + private static boolean containsFlag(int haystack, int needle) { + return (haystack & needle) != 0; + } +} diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 0a124701fc0a..e0138c5db178 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2511,9 +2511,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>Not all output formats may be supported in a configuration with * an input stream of a particular format. For more details, see * android.scaler.availableInputOutputFormatsMap.</p> - * <p>The following table describes the minimum required output stream - * configurations based on the hardware level - * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}), prior to Android 12:</p> + * <p>For applications targeting SDK version older than 31, the following table + * describes the minimum required output stream configurations based on the hardware level + * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}):</p> * <table> * <thead> * <tr> @@ -2574,10 +2574,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * </tr> * </tbody> * </table> - * <p>Starting from Android 12, the camera device may not support JPEG sizes smaller than the - * minimum of 1080p and the camera sensor active array size. The requirements for - * IMPLEMENTATION_DEFINED and YUV_420_888 stay the same. This new minimum required output - * stream configurations are illustrated by the table below:</p> + * <p>For applications targeting SDK version 31 or newer, if the mobile device declares to be + * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS media performance class} S, + * the primary camera devices (first rear/front camera in the camera ID list) will not + * support JPEG sizes smaller than 1080p. If the application configures a JPEG stream + * smaller than 1080p, the camera device will round up the JPEG image size to at least + * 1080p. The requirements for IMPLEMENTATION_DEFINED and YUV_420_888 stay the same. + * This new minimum required output stream configurations are illustrated by the table below:</p> * <table> * <thead> * <tr> @@ -2644,6 +2647,10 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * </tr> * </tbody> * </table> + * <p>For applications targeting SDK version 31 or newer, if the mobile device doesn't declare + * to be media performance class S, or if the camera device isn't a primary rear/front + * camera, the minimum required output stream configurations are the same as for applications + * targeting SDK version older than 31.</p> * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional * mandatory stream configurations on a per-capability basis.</p> * <p>Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index 41272437c407..e24332a1885a 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.graphics.ImageFormat; import android.hardware.camera2.extension.IAdvancedExtenderImpl; import android.hardware.camera2.extension.ICameraExtensionsProxyService; @@ -32,6 +33,7 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.os.ConditionVariable; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemProperties; import android.annotation.IntDef; import android.annotation.NonNull; import android.util.Log; @@ -235,6 +237,19 @@ public final class CameraExtensionCharacteristics { if (mConnection == null) { Intent intent = new Intent(); intent.setClassName(PROXY_PACKAGE_NAME, PROXY_SERVICE_NAME); + String vendorProxyPackage = SystemProperties.get( + "ro.vendor.camera.extensions.package"); + String vendorProxyService = SystemProperties.get( + "ro.vendor.camera.extensions.service"); + if (!vendorProxyPackage.isEmpty() && !vendorProxyService.isEmpty()) { + Log.v(TAG, + "Choosing the vendor camera extensions proxy package: " + + vendorProxyPackage); + Log.v(TAG, + "Choosing the vendor camera extensions proxy service: " + + vendorProxyService); + intent.setClassName(vendorProxyPackage, vendorProxyService); + } mInitFuture = new InitializerFuture(); mConnection = new ServiceConnection() { @Override @@ -255,9 +270,9 @@ public final class CameraExtensionCharacteristics { } } }; - ctx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE | - Context.BIND_IMPORTANT | Context.BIND_ABOVE_CLIENT | - Context.BIND_NOT_VISIBLE); + ctx.bindService(intent, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | + Context.BIND_ABOVE_CLIENT | Context.BIND_NOT_VISIBLE, + android.os.AsyncTask.THREAD_POOL_EXECUTOR, mConnection); try { mInitFuture.get(PROXY_SERVICE_DELAY_MS, TimeUnit.MILLISECONDS); diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index c1af99a82bc8..a1c8d29566be 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -217,7 +217,7 @@ public final class CameraManager { @NonNull Map<String, SessionConfiguration> cameraIdAndSessionConfig) throws CameraAccessException { return CameraManagerGlobal.get().isConcurrentSessionConfigurationSupported( - cameraIdAndSessionConfig); + cameraIdAndSessionConfig, mContext.getApplicationInfo().targetSdkVersion); } /** @@ -413,7 +413,8 @@ public final class CameraManager { try { for (String physicalCameraId : physicalCameraIds) { CameraMetadataNative physicalCameraInfo = - cameraService.getCameraCharacteristics(physicalCameraId); + cameraService.getCameraCharacteristics(physicalCameraId, + mContext.getApplicationInfo().targetSdkVersion); StreamConfiguration[] configs = physicalCameraInfo.get( CameraCharacteristics. SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS); @@ -472,7 +473,8 @@ public final class CameraManager { try { Size displaySize = getDisplaySize(); - CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId); + CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId, + mContext.getApplicationInfo().targetSdkVersion); try { info.setCameraId(Integer.parseInt(cameraId)); } catch (NumberFormatException e) { @@ -590,7 +592,7 @@ public final class CameraManager { } cameraUser = cameraService.connectDevice(callbacks, cameraId, mContext.getOpPackageName(), mContext.getAttributionTag(), uid, - oomScoreOffset); + oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion); } catch (ServiceSpecificException e) { if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) { throw new AssertionError("Should've gone down the shim path"); @@ -1613,8 +1615,8 @@ public final class CameraManager { } public boolean isConcurrentSessionConfigurationSupported( - @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations) - throws CameraAccessException { + @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations, + int targetSdkVersion) throws CameraAccessException { if (cameraIdsAndSessionConfigurations == null) { throw new IllegalArgumentException("cameraIdsAndSessionConfigurations was null"); @@ -1650,7 +1652,7 @@ public final class CameraManager { } try { return mCameraService.isConcurrentSessionConfigurationSupported( - cameraIdsAndConfigs); + cameraIdsAndConfigs, targetSdkVersion); } catch (ServiceSpecificException e) { throwAsPublicException(e); } catch (RemoteException e) { diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index c739c6a53167..1c0ae281d9fe 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -16,24 +16,41 @@ package android.hardware.display; +import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.Point; import android.hardware.SensorManager; import android.os.Handler; import android.os.PowerManager; import android.util.IntArray; +import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Objects; + /** * Display manager local system service interface. * * @hide Only for use within the system server. */ public abstract class DisplayManagerInternal { + + @IntDef(prefix = {"REFRESH_RATE_LIMIT_"}, value = { + REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RefreshRateLimitType {} + + /** Refresh rate should be limited when High Brightness Mode is active. */ + public static final int REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE = 1; + /** * Called by the power manager to initialize power management facilities. */ @@ -293,6 +310,33 @@ public abstract class DisplayManagerInternal { public abstract int getRefreshRateSwitchingType(); /** + * TODO: b/191384041 - Replace this with getRefreshRateLimitations() + * Return the refresh rate restriction for the specified display and sensor pairing. If the + * specified sensor is identified as an associated sensor in the specified display's + * display-device-config file, then return any refresh rate restrictions that it might define. + * If no restriction is specified, or the sensor is not associated with the display, then null + * will be returned. + * + * @param displayId The display to check against. + * @param name The name of the sensor. + * @param type The type of sensor. + * + * @return The min/max refresh-rate restriction as a {@link Pair} of floats, or null if not + * restricted. + */ + public abstract RefreshRateRange getRefreshRateForDisplayAndSensor( + int displayId, String name, String type); + + /** + * Returns a list of various refresh rate limitations for the specified display. + * + * @param displayId The display to get limitations for. + * + * @return a list of {@link RefreshRateLimitation}s describing the various limits. + */ + public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId); + + /** * Describes the requested power state of the display. * * This object is intended to describe the general characteristics of the @@ -527,4 +571,91 @@ public abstract class DisplayManagerInternal { */ void onDisplayGroupChanged(int groupId); } + + /** + * Information about the min and max refresh rate DM would like to set the display to. + */ + public static final class RefreshRateRange { + public static final String TAG = "RefreshRateRange"; + + // The tolerance within which we consider something approximately equals. + public static final float FLOAT_TOLERANCE = 0.01f; + + /** + * The lowest desired refresh rate. + */ + public float min; + + /** + * The highest desired refresh rate. + */ + public float max; + + public RefreshRateRange() {} + + public RefreshRateRange(float min, float max) { + if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) { + Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " + + min + " " + max); + this.min = this.max = 0; + return; + } + if (min > max) { + // Min and max are within epsilon of each other, but in the wrong order. + float t = min; + min = max; + max = t; + } + this.min = min; + this.max = max; + } + + /** + * Checks whether the two objects have the same values. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof RefreshRateRange)) { + return false; + } + + RefreshRateRange refreshRateRange = (RefreshRateRange) other; + return (min == refreshRateRange.min && max == refreshRateRange.max); + } + + @Override + public int hashCode() { + return Objects.hash(min, max); + } + + @Override + public String toString() { + return "(" + min + " " + max + ")"; + } + } + + /** + * Describes a limitation on a display's refresh rate. Includes the allowed refresh rate + * range as well as information about when it applies, such as high-brightness-mode. + */ + public static final class RefreshRateLimitation { + @RefreshRateLimitType public int type; + + /** The range the that refresh rate should be limited to. */ + public RefreshRateRange range; + + public RefreshRateLimitation(@RefreshRateLimitType int type, float min, float max) { + this.type = type; + range = new RefreshRateRange(min, max); + } + + @Override + public String toString() { + return "RefreshRateLimitation(" + type + ": " + range + ")"; + } + } } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index e5946663df47..688f9f1174dd 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1375,6 +1375,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: return context.getString( com.android.internal.R.string.fingerprint_error_security_update_required); + case FINGERPRINT_ERROR_BAD_CALIBARTION: + context.getString( + com.android.internal.R.string.fingerprint_error_bad_calibration); case FINGERPRINT_ERROR_VENDOR: { String[] msgArray = context.getResources().getStringArray( com.android.internal.R.array.fingerprint_error_vendor); diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java index ee86265e81ad..802387c675b5 100644 --- a/core/java/android/os/AggregateBatteryConsumer.java +++ b/core/java/android/os/AggregateBatteryConsumer.java @@ -17,7 +17,13 @@ package android.os; import android.annotation.NonNull; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; /** @@ -72,6 +78,43 @@ public final class AggregateBatteryConsumer extends BatteryConsumer implements P return mConsumedPowerMah; } + /** Serializes this object to XML */ + void writeToXml(TypedXmlSerializer serializer, + @BatteryUsageStats.AggregateBatteryConsumerScope int scope) throws IOException { + serializer.startTag(null, BatteryUsageStats.XML_TAG_AGGREGATE); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE, scope); + serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, mConsumedPowerMah); + mPowerComponents.writeToXml(serializer); + serializer.endTag(null, BatteryUsageStats.XML_TAG_AGGREGATE); + } + + /** Parses an XML representation and populates the BatteryUsageStats builder */ + static void parseXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder) + throws XmlPullParserException, IOException { + final int scope = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE); + final Builder consumerBuilder = builder.getAggregateBatteryConsumerBuilder(scope); + + int eventType = parser.getEventType(); + if (eventType != XmlPullParser.START_TAG || !parser.getName().equals( + BatteryUsageStats.XML_TAG_AGGREGATE)) { + throw new XmlPullParserException("Invalid XML parser state"); + } + + consumerBuilder.setConsumedPower( + parser.getAttributeDouble(null, BatteryUsageStats.XML_ATTR_POWER)); + + while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals( + BatteryUsageStats.XML_TAG_AGGREGATE)) + && eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { + PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder); + } + } + eventType = parser.next(); + } + } + /** * Builder for DeviceBatteryConsumer. */ @@ -91,6 +134,14 @@ public final class AggregateBatteryConsumer extends BatteryConsumer implements P } /** + * Adds power and usage duration from the supplied AggregateBatteryConsumer. + */ + public void add(AggregateBatteryConsumer aggregateBatteryConsumer) { + mConsumedPowerMah += aggregateBatteryConsumer.mConsumedPowerMah; + mPowerComponentsBuilder.addPowerAndDuration(aggregateBatteryConsumer.mPowerComponents); + } + + /** * Creates a read-only object out of the Builder values. */ @NonNull diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 6c9f0f677db9..f48375246616 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -20,16 +20,23 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.util.Range; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.os.BatteryStatsHistory; import com.android.internal.os.BatteryStatsHistoryIterator; import com.android.internal.os.PowerCalculator; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.List; @@ -77,6 +84,34 @@ public final class BatteryUsageStats implements Parcelable { private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000; + // XML tags and attributes for BatteryUsageStats persistence + static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats"; + static final String XML_TAG_AGGREGATE = "aggregate"; + static final String XML_TAG_UID = "uid"; + static final String XML_TAG_USER = "user"; + static final String XML_TAG_POWER_COMPONENTS = "power_components"; + static final String XML_TAG_COMPONENT = "component"; + static final String XML_TAG_CUSTOM_COMPONENT = "custom_component"; + static final String XML_ATTR_ID = "id"; + static final String XML_ATTR_UID = "uid"; + static final String XML_ATTR_USER_ID = "user_id"; + static final String XML_ATTR_SCOPE = "scope"; + static final String XML_ATTR_PREFIX_CUSTOM_COMPONENT = "custom_component_"; + static final String XML_ATTR_START_TIMESTAMP = "start_timestamp"; + static final String XML_ATTR_END_TIMESTAMP = "end_timestamp"; + static final String XML_ATTR_POWER = "power"; + static final String XML_ATTR_DURATION = "duration"; + static final String XML_ATTR_MODEL = "model"; + static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity"; + static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct"; + static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower"; + static final String XML_ATTR_DISCHARGE_UPPER = "discharge_upper"; + static final String XML_ATTR_BATTERY_REMAINING = "battery_remaining"; + static final String XML_ATTR_CHARGE_REMAINING = "charge_remaining"; + static final String XML_ATTR_HIGHEST_DRAIN_PACKAGE = "highest_drain_package"; + static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground"; + static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background"; + private final int mDischargePercentage; private final double mBatteryCapacityMah; private final long mStatsStartTimestampMs; @@ -96,11 +131,7 @@ public final class BatteryUsageStats implements Parcelable { private BatteryUsageStats(@NonNull Builder builder) { mStatsStartTimestampMs = builder.mStatsStartTimestampMs; mStatsEndTimestampMs = builder.mStatsEndTimestampMs; - if (builder.mStatsDurationMs != -1) { - mStatsDurationMs = builder.mStatsDurationMs; - } else { - mStatsDurationMs = mStatsEndTimestampMs - mStatsStartTimestampMs; - } + mStatsDurationMs = builder.getStatsDuration(); mBatteryCapacityMah = builder.mBatteryCapacityMah; mDischargePercentage = builder.mDischargePercentage; mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah; @@ -275,14 +306,23 @@ public final class BatteryUsageStats implements Parcelable { AggregateBatteryConsumer.CREATOR.createFromParcel(source); mAggregateBatteryConsumers[i].setCustomPowerComponentNames(mCustomPowerComponentNames); } - int uidCount = source.readInt(); + + // UidBatteryConsumers are included as a blob to avoid a TransactionTooLargeException + final Parcel blob = Parcel.obtain(); + final byte[] bytes = source.readBlob(); + blob.unmarshall(bytes, 0, bytes.length); + blob.setDataPosition(0); + + final int uidCount = blob.readInt(); mUidBatteryConsumers = new ArrayList<>(uidCount); for (int i = 0; i < uidCount; i++) { final UidBatteryConsumer consumer = - UidBatteryConsumer.CREATOR.createFromParcel(source); + UidBatteryConsumer.CREATOR.createFromParcel(blob); consumer.setCustomPowerComponentNames(mCustomPowerComponentNames); mUidBatteryConsumers.add(consumer); } + blob.recycle(); + int userCount = source.readInt(); mUserBatteryConsumers = new ArrayList<>(userCount); for (int i = 0; i < userCount; i++) { @@ -292,14 +332,10 @@ public final class BatteryUsageStats implements Parcelable { mUserBatteryConsumers.add(consumer); } if (source.readBoolean()) { - mHistoryBuffer = Parcel.obtain(); - mHistoryBuffer.setDataSize(0); - mHistoryBuffer.setDataPosition(0); + final byte[] historyBlob = source.readBlob(); - int historyBufferSize = source.readInt(); - int curPos = source.dataPosition(); - mHistoryBuffer.appendFrom(source, curPos, historyBufferSize); - source.setDataPosition(curPos + historyBufferSize); + mHistoryBuffer = Parcel.obtain(); + mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length); int historyTagCount = source.readInt(); mHistoryTagPool = new ArrayList<>(historyTagCount); @@ -331,21 +367,26 @@ public final class BatteryUsageStats implements Parcelable { for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) { mAggregateBatteryConsumers[i].writeToParcel(dest, flags); } - dest.writeInt(mUidBatteryConsumers.size()); + + // UidBatteryConsumers are included as a blob, because each UidBatteryConsumer + // takes > 300 bytes, so a typical number of UIDs in the system, 300 would result + // in a 90 kB Parcel, which is not safe to pass in a binder transaction because + // of the possibility of TransactionTooLargeException + final Parcel blob = Parcel.obtain(); + blob.writeInt(mUidBatteryConsumers.size()); for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) { - mUidBatteryConsumers.get(i).writeToParcel(dest, flags); + mUidBatteryConsumers.get(i).writeToParcel(blob, flags); } + dest.writeBlob(blob.marshall()); + blob.recycle(); + dest.writeInt(mUserBatteryConsumers.size()); for (int i = mUserBatteryConsumers.size() - 1; i >= 0; i--) { mUserBatteryConsumers.get(i).writeToParcel(dest, flags); } if (mHistoryBuffer != null) { dest.writeBoolean(true); - - final int historyBufferSize = mHistoryBuffer.dataSize(); - dest.writeInt(historyBufferSize); - dest.appendFrom(mHistoryBuffer, 0, historyBufferSize); - + dest.writeBlob(mHistoryBuffer.marshall()); dest.writeInt(mHistoryTagPool.size()); for (int i = mHistoryTagPool.size() - 1; i >= 0; i--) { final BatteryStats.HistoryTag tag = mHistoryTagPool.get(i); @@ -579,6 +620,109 @@ public final class BatteryUsageStats implements Parcelable { } } + /** Serializes this object to XML */ + public void writeXml(TypedXmlSerializer serializer) throws IOException { + serializer.startTag(null, XML_TAG_BATTERY_USAGE_STATS); + + for (int i = 0; i < mCustomPowerComponentNames.length; i++) { + serializer.attribute(null, XML_ATTR_PREFIX_CUSTOM_COMPONENT + i, + mCustomPowerComponentNames[i]); + } + + serializer.attributeLong(null, XML_ATTR_START_TIMESTAMP, mStatsStartTimestampMs); + serializer.attributeLong(null, XML_ATTR_END_TIMESTAMP, mStatsEndTimestampMs); + serializer.attributeLong(null, XML_ATTR_DURATION, mStatsDurationMs); + serializer.attributeDouble(null, XML_ATTR_BATTERY_CAPACITY, mBatteryCapacityMah); + serializer.attributeInt(null, XML_ATTR_DISCHARGE_PERCENT, mDischargePercentage); + serializer.attributeDouble(null, XML_ATTR_DISCHARGE_LOWER, mDischargedPowerLowerBound); + serializer.attributeDouble(null, XML_ATTR_DISCHARGE_UPPER, mDischargedPowerUpperBound); + serializer.attributeLong(null, XML_ATTR_BATTERY_REMAINING, mBatteryTimeRemainingMs); + serializer.attributeLong(null, XML_ATTR_CHARGE_REMAINING, mChargeTimeRemainingMs); + + for (int scope = 0; scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; + scope++) { + mAggregateBatteryConsumers[scope].writeToXml(serializer, scope); + } + for (UidBatteryConsumer consumer : mUidBatteryConsumers) { + consumer.writeToXml(serializer); + } + for (UserBatteryConsumer consumer : mUserBatteryConsumers) { + consumer.writeToXml(serializer); + } + serializer.endTag(null, XML_TAG_BATTERY_USAGE_STATS); + } + + /** Parses an XML representation of BatteryUsageStats */ + public static BatteryUsageStats createFromXml(TypedXmlPullParser parser) + throws XmlPullParserException, IOException { + Builder builder = null; + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG + && parser.getName().equals(XML_TAG_BATTERY_USAGE_STATS)) { + List<String> customComponentNames = new ArrayList<>(); + int i = 0; + while (true) { + int index = parser.getAttributeIndex(null, + XML_ATTR_PREFIX_CUSTOM_COMPONENT + i); + if (index == -1) { + break; + } + customComponentNames.add(parser.getAttributeValue(index)); + i++; + } + + builder = new Builder( + customComponentNames.toArray(new String[0]), true); + + builder.setStatsStartTimestamp( + parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP)); + builder.setStatsEndTimestamp( + parser.getAttributeLong(null, XML_ATTR_END_TIMESTAMP)); + builder.setStatsDuration( + parser.getAttributeLong(null, XML_ATTR_DURATION)); + builder.setBatteryCapacity( + parser.getAttributeDouble(null, XML_ATTR_BATTERY_CAPACITY)); + builder.setDischargePercentage( + parser.getAttributeInt(null, XML_ATTR_DISCHARGE_PERCENT)); + builder.setDischargedPowerRange( + parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_LOWER), + parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_UPPER)); + builder.setBatteryTimeRemainingMs( + parser.getAttributeLong(null, XML_ATTR_BATTERY_REMAINING)); + builder.setChargeTimeRemainingMs( + parser.getAttributeLong(null, XML_ATTR_CHARGE_REMAINING)); + + eventType = parser.next(); + break; + } + eventType = parser.next(); + } + + if (builder == null) { + throw new XmlPullParserException("No root element"); + } + + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + switch (parser.getName()) { + case XML_TAG_AGGREGATE: + AggregateBatteryConsumer.parseXml(parser, builder); + break; + case XML_TAG_UID: + UidBatteryConsumer.createFromXml(parser, builder); + break; + case XML_TAG_USER: + UserBatteryConsumer.createFromXml(parser, builder); + break; + } + } + eventType = parser.next(); + } + + return builder.build(); + } + /** * Builder for BatteryUsageStats. */ @@ -658,6 +802,14 @@ public final class BatteryUsageStats implements Parcelable { return this; } + private long getStatsDuration() { + if (mStatsDurationMs != -1) { + return mStatsDurationMs; + } else { + return mStatsEndTimestampMs - mStatsStartTimestampMs; + } + } + /** * Sets the battery discharge amount since BatteryStats reset as percentage of the full * charge. @@ -738,6 +890,22 @@ public final class BatteryUsageStats implements Parcelable { } /** + * Creates or returns a UidBatteryConsumer, which represents battery attribution + * data for an individual UID. This version of the method is not suitable for use + * with PowerCalculators. + */ + @NonNull + public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(int uid) { + UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid); + if (builder == null) { + builder = new UidBatteryConsumer.Builder(mCustomPowerComponentNames, + mIncludePowerModels, uid); + mUidBatteryConsumerBuilders.put(uid, builder); + } + return builder; + } + + /** * Creates or returns a UserBatteryConsumer, which represents battery attribution * data for an individual {@link UserHandle}. */ @@ -756,5 +924,59 @@ public final class BatteryUsageStats implements Parcelable { public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() { return mUidBatteryConsumerBuilders; } + + /** + * Adds battery usage stats from another snapshots. The two snapshots are assumed to be + * non-overlapping, meaning that the power consumption estimates and session durations + * can be simply summed across the two snapshots. This remains true even if the timestamps + * seem to indicate that the sessions are in fact overlapping: timestamps may be off as a + * result of realtime clock adjustments by the user or the system. + */ + @NonNull + public Builder add(BatteryUsageStats stats) { + if (!Arrays.equals(mCustomPowerComponentNames, stats.mCustomPowerComponentNames)) { + throw new IllegalArgumentException( + "BatteryUsageStats have different custom power components"); + } + + if (mUserBatteryConsumerBuilders.size() != 0 + || !stats.getUserBatteryConsumers().isEmpty()) { + throw new UnsupportedOperationException( + "Combining UserBatteryConsumers is not supported"); + } + + mDischargedPowerLowerBoundMah += stats.mDischargedPowerLowerBound; + mDischargedPowerUpperBoundMah += stats.mDischargedPowerUpperBound; + mDischargePercentage += stats.mDischargePercentage; + + mStatsDurationMs = getStatsDuration() + stats.getStatsDuration(); + + if (mStatsStartTimestampMs == 0 + || stats.mStatsStartTimestampMs < mStatsStartTimestampMs) { + mStatsStartTimestampMs = stats.mStatsStartTimestampMs; + } + + final boolean addingLaterSnapshot = stats.mStatsEndTimestampMs > mStatsEndTimestampMs; + if (addingLaterSnapshot) { + mStatsEndTimestampMs = stats.mStatsEndTimestampMs; + } + + for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) { + getAggregateBatteryConsumerBuilder(scope) + .add(stats.mAggregateBatteryConsumers[scope]); + } + + for (UidBatteryConsumer consumer : stats.getUidBatteryConsumers()) { + getOrCreateUidBatteryConsumerBuilder(consumer.getUid()).add(consumer); + } + + if (addingLaterSnapshot) { + mBatteryCapacityMah = stats.mBatteryCapacityMah; + mBatteryTimeRemainingMs = stats.mBatteryTimeRemainingMs; + mChargeTimeRemainingMs = stats.mChargeTimeRemainingMs; + } + + return this; + } } } diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index 50804422e92f..97f24ccecaee 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -72,12 +72,16 @@ public final class BatteryUsageStatsQuery implements Parcelable { @NonNull private final int[] mUserIds; private final long mMaxStatsAgeMs; + private long mFromTimestamp; + private long mToTimestamp; private BatteryUsageStatsQuery(@NonNull Builder builder) { mFlags = builder.mFlags; mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray() : new int[]{UserHandle.USER_ALL}; mMaxStatsAgeMs = builder.mMaxStatsAgeMs; + mFromTimestamp = builder.mFromTimestamp; + mToTimestamp = builder.mToTimestamp; } @BatteryUsageStatsFlags @@ -112,11 +116,30 @@ public final class BatteryUsageStatsQuery implements Parcelable { return mMaxStatsAgeMs; } + /** + * Returns the exclusive lower bound of the stored snapshot timestamps that should be included + * in the aggregation. Ignored if {@link #getToTimestamp()} is zero. + */ + public long getFromTimestamp() { + return mFromTimestamp; + } + + /** + * Returns the inclusive upper bound of the stored snapshot timestamps that should + * be included in the aggregation. The default is to include only the current stats + * accumulated since the latest battery reset. + */ + public long getToTimestamp() { + return mToTimestamp; + } + private BatteryUsageStatsQuery(Parcel in) { mFlags = in.readInt(); mUserIds = new int[in.readInt()]; in.readIntArray(mUserIds); mMaxStatsAgeMs = in.readLong(); + mFromTimestamp = in.readLong(); + mToTimestamp = in.readLong(); } @Override @@ -125,6 +148,8 @@ public final class BatteryUsageStatsQuery implements Parcelable { dest.writeInt(mUserIds.length); dest.writeIntArray(mUserIds); dest.writeLong(mMaxStatsAgeMs); + dest.writeLong(mFromTimestamp); + dest.writeLong(mToTimestamp); } @Override @@ -153,6 +178,8 @@ public final class BatteryUsageStatsQuery implements Parcelable { private int mFlags; private IntArray mUserIds; private long mMaxStatsAgeMs = DEFAULT_MAX_STATS_AGE_MS; + private long mFromTimestamp; + private long mToTimestamp; /** * Builds a read-only BatteryUsageStatsQuery object. @@ -204,6 +231,17 @@ public final class BatteryUsageStatsQuery implements Parcelable { } /** + * Requests to aggregate stored snapshots between the two supplied timestamps + * @param fromTimestamp Exclusive starting timestamp, as per System.currentTimeMillis() + * @param toTimestamp Inclusive ending timestamp, as per System.currentTimeMillis() + */ + public Builder aggregateSnapshots(long fromTimestamp, long toTimestamp) { + mFromTimestamp = fromTimestamp; + mToTimestamp = toTimestamp; + return this; + } + + /** * Set the client's tolerance for stale battery stats. The data may be up to * this many milliseconds out-of-date. */ diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 9a8194240f8e..3bee4b73dc22 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -46,6 +46,7 @@ interface IUserManager { UserInfo createProfileForUserWithThrow(in String name, in String userType, int flags, int userId, in String[] disallowedPackages); UserInfo createRestrictedProfileWithThrow(String name, int parentUserHandle); + String[] getPreInstallableSystemPackages(in String userType); void setUserEnabled(int userId); void setUserAdmin(int userId); void evictCredentialEncryptionKey(int userId); diff --git a/core/java/android/os/PackageTagsList.java b/core/java/android/os/PackageTagsList.java index c94d3de33b6f..4a81dc6f592e 100644 --- a/core/java/android/os/PackageTagsList.java +++ b/core/java/android/os/PackageTagsList.java @@ -23,20 +23,26 @@ import android.annotation.TestApi; import android.util.ArrayMap; import android.util.ArraySet; +import com.android.internal.annotations.Immutable; + import java.io.PrintWriter; +import java.util.Collection; import java.util.Map; import java.util.Objects; import java.util.Set; /** - * A list of packages and associated attribution tags that supports easy membership checks. + * A list of packages and associated attribution tags that supports easy membership checks. Supports + * "wildcard" attribution tags (ie, matching any attribution tag under a package) in additional to + * standard checks. * * @hide */ @TestApi +@Immutable public final class PackageTagsList implements Parcelable { - // an empty set value matches any attribution tag + // an empty set value matches any attribution tag (ie, wildcard) private final ArrayMap<String, ArraySet<String>> mPackageTags; private PackageTagsList(@NonNull ArrayMap<String, ArraySet<String>> packageTags) { @@ -51,15 +57,34 @@ public final class PackageTagsList implements Parcelable { } /** - * Returns true if the given package is represented within this instance. If this returns true - * this does not imply anything about whether any given attribution tag under the given package - * name is present. + * Returns true if the given package is found within this instance. If this returns true this + * does not imply anything about whether any given attribution tag under the given package name + * is present. */ public boolean includes(@NonNull String packageName) { return mPackageTags.containsKey(packageName); } /** + * Returns true if the given attribution tag is found within this instance under any package. + * Only returns true if the attribution tag literal is found, not if any package contains the + * set of all attribution tags. + * + * @hide + */ + public boolean includesTag(@NonNull String attributionTag) { + final int size = mPackageTags.size(); + for (int i = 0; i < size; i++) { + ArraySet<String> tags = mPackageTags.valueAt(i); + if (tags.contains(attributionTag)) { + return true; + } + } + + return false; + } + + /** * Returns true if all attribution tags under the given package are contained within this * instance. */ @@ -76,6 +101,7 @@ public final class PackageTagsList implements Parcelable { if (tags == null) { return false; } else if (tags.isEmpty()) { + // our tags are the full set, so we contain any attribution tag return true; } else { return tags.contains(attributionTag); @@ -98,10 +124,12 @@ public final class PackageTagsList implements Parcelable { return false; } if (tags.isEmpty()) { + // our tags are the full set, so we contain whatever the other tags are continue; } ArraySet<String> otherTags = packageTagsList.mPackageTags.valueAt(i); if (otherTags.isEmpty()) { + // other tags are the full set, so we can't contain them return false; } if (!tags.containsAll(otherTags)) { @@ -248,6 +276,31 @@ public final class PackageTagsList implements Parcelable { } /** + * Adds the specified package and set of attribution tags to the builder. + * + * @hide + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder add(@NonNull String packageName, + @NonNull Collection<String> attributionTags) { + if (attributionTags.isEmpty()) { + // the input is not allowed to specify a full set by passing in an empty collection + return this; + } + + ArraySet<String> tags = mPackageTags.get(packageName); + if (tags == null) { + tags = new ArraySet<>(attributionTags); + mPackageTags.put(packageName, tags); + } else if (!tags.isEmpty()) { + // if we contain the full set, already done, otherwise add all the tags + tags.addAll(attributionTags); + } + + return this; + } + + /** * Adds the specified {@link PackageTagsList} to the builder. */ @SuppressLint("MissingGetterMatchingBuilder") @@ -267,13 +320,92 @@ public final class PackageTagsList implements Parcelable { if (newTags.isEmpty()) { add(entry.getKey()); } else { - ArraySet<String> tags = mPackageTags.get(entry.getKey()); - if (tags == null) { - tags = new ArraySet<>(newTags); - mPackageTags.put(entry.getKey(), tags); - } else if (!tags.isEmpty()) { - tags.addAll(newTags); - } + add(entry.getKey(), newTags); + } + } + + return this; + } + + /** + * Removes all attribution tags under the specified package from the builder. + * + * @hide + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder remove(@NonNull String packageName) { + mPackageTags.remove(packageName); + return this; + } + + /** + * Removes the specified package and attribution tag from the builder if and only if the + * specified attribution tag is listed explicitly under the package. If the package contains + * all possible attribution tags, then nothing will be removed. + * + * @hide + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder remove(@NonNull String packageName, + @Nullable String attributionTag) { + ArraySet<String> tags = mPackageTags.get(packageName); + if (tags != null && tags.remove(attributionTag) && tags.isEmpty()) { + mPackageTags.remove(packageName); + } + return this; + } + + /** + * Removes the specified package and set of attribution tags from the builder if and only if + * the specified set of attribution tags are listed explicitly under the package. If the + * package contains all possible attribution tags, then nothing will be removed. + * + * @hide + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder remove(@NonNull String packageName, + @NonNull Collection<String> attributionTags) { + if (attributionTags.isEmpty()) { + // the input is not allowed to specify a full set by passing in an empty collection + return this; + } + + ArraySet<String> tags = mPackageTags.get(packageName); + if (tags != null && tags.removeAll(attributionTags) && tags.isEmpty()) { + mPackageTags.remove(packageName); + } + return this; + } + + /** + * Removes the specified {@link PackageTagsList} from the builder. If a package contains all + * possible attribution tags, it will only be removed if the package in the removed + * {@link PackageTagsList} also contains all possible attribution tags. + * + * @hide + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder remove(@NonNull PackageTagsList packageTagsList) { + return remove(packageTagsList.mPackageTags); + } + + /** + * Removes the given map of package to attribution tags to the builder. An empty set of + * attribution tags is interpreted to imply all attribution tags under that package. If a + * package contains all possible attribution tags, it will only be removed if the package in + * the removed map also contains all possible attribution tags. + * + * @hide + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder remove(@NonNull Map<String, ? extends Set<String>> packageTagsMap) { + for (Map.Entry<String, ? extends Set<String>> entry : packageTagsMap.entrySet()) { + Set<String> removedTags = entry.getValue(); + if (removedTags.isEmpty()) { + // if removing the full set, drop the package completely + remove(entry.getKey()); + } else { + remove(entry.getKey(), removedTags); } } diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index a90ed20d54fc..db3d13bdb07b 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -19,11 +19,18 @@ import static android.os.BatteryConsumer.convertMahToDeciCoulombs; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.os.PowerCalculator; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; +import java.util.Arrays; /** * Contains details of battery attribution data broken down to individual power drain types @@ -36,9 +43,12 @@ class PowerComponents { - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; private final double mConsumedPowerMah; + @NonNull private final double[] mPowerComponentsMah; + @NonNull private final long[] mUsageDurationsMs; private final int mCustomPowerComponentCount; + @Nullable private final byte[] mPowerModels; // Not written to Parcel and must be explicitly restored during the parent object's unparceling private String[] mCustomPowerComponentNames; @@ -49,7 +59,7 @@ class PowerComponents { mPowerComponentsMah = builder.mPowerComponentsMah; mUsageDurationsMs = builder.mUsageDurationsMs; mConsumedPowerMah = builder.getTotalPower(); - mPowerModels = builder.mPowerModels; + mPowerModels = builder.getPowerModels(); } PowerComponents(@NonNull Parcel source) { @@ -146,9 +156,13 @@ class PowerComponents { } } + public boolean hasPowerModels() { + return mPowerModels != null; + } + @BatteryConsumer.PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int component) { - if (mPowerModels == null) { + if (!hasPowerModels()) { throw new IllegalStateException( "Power model IDs were not requested in the BatteryUsageStatsQuery"); } @@ -294,10 +308,128 @@ class PowerComponents { return interestingData; } + void writeToXml(TypedXmlSerializer serializer) throws IOException { + serializer.startTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); + for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; + componentId++) { + final double powerMah = getConsumedPower(componentId); + final long durationMs = getUsageDurationMillis(componentId); + if (powerMah == 0 && durationMs == 0) { + continue; + } + + serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId); + if (powerMah != 0) { + serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah); + } + if (durationMs != 0) { + serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs); + } + if (mPowerModels != null) { + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL, + mPowerModels[componentId]); + } + serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT); + } + + final int customComponentEnd = + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + mCustomPowerComponentCount; + for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; + componentId < customComponentEnd; + componentId++) { + final double powerMah = getConsumedPowerForCustomComponent(componentId); + final long durationMs = getUsageDurationForCustomComponentMillis(componentId); + if (powerMah == 0 && durationMs == 0) { + continue; + } + + serializer.startTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId); + if (powerMah != 0) { + serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah); + } + if (durationMs != 0) { + serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs); + } + serializer.endTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT); + } + + serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); + } + + + static void parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder) + throws XmlPullParserException, IOException { + int eventType = parser.getEventType(); + if (eventType != XmlPullParser.START_TAG || !parser.getName().equals( + BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { + throw new XmlPullParserException("Invalid XML parser state"); + } + + while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals( + BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) + && eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + switch (parser.getName()) { + case BatteryUsageStats.XML_TAG_COMPONENT: { + int componentId = -1; + double powerMah = 0; + long durationMs = 0; + int model = BatteryConsumer.POWER_MODEL_UNDEFINED; + for (int i = 0; i < parser.getAttributeCount(); i++) { + switch (parser.getAttributeName(i)) { + case BatteryUsageStats.XML_ATTR_ID: + componentId = parser.getAttributeInt(i); + break; + case BatteryUsageStats.XML_ATTR_POWER: + powerMah = parser.getAttributeDouble(i); + break; + case BatteryUsageStats.XML_ATTR_DURATION: + durationMs = parser.getAttributeLong(i); + break; + case BatteryUsageStats.XML_ATTR_MODEL: + model = parser.getAttributeInt(i); + break; + } + } + builder.setConsumedPower(componentId, powerMah, model); + builder.setUsageDurationMillis(componentId, durationMs); + break; + } + case BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT: { + int componentId = -1; + double powerMah = 0; + long durationMs = 0; + for (int i = 0; i < parser.getAttributeCount(); i++) { + switch (parser.getAttributeName(i)) { + case BatteryUsageStats.XML_ATTR_ID: + componentId = parser.getAttributeInt(i); + break; + case BatteryUsageStats.XML_ATTR_POWER: + powerMah = parser.getAttributeDouble(i); + break; + case BatteryUsageStats.XML_ATTR_DURATION: + durationMs = parser.getAttributeLong(i); + break; + } + } + builder.setConsumedPowerForCustomComponent(componentId, powerMah); + builder.setUsageDurationForCustomComponentMillis(componentId, durationMs); + break; + } + } + } + eventType = parser.next(); + } + } + /** * Builder for PowerComponents. */ static final class Builder { + private static final byte POWER_MODEL_UNINITIALIZED = -1; + private final double[] mPowerComponentsMah; private final String[] mCustomPowerComponentNames; private final long[] mUsageDurationsMs; @@ -311,6 +443,7 @@ class PowerComponents { mUsageDurationsMs = new long[powerComponentCount]; if (includePowerModels) { mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT]; + Arrays.fill(mPowerModels, POWER_MODEL_UNINITIALIZED); } else { mPowerModels = null; } @@ -412,12 +545,39 @@ class PowerComponents { return this; } - public void addPowerAndDuration(Builder other) { + public void addPowerAndDuration(PowerComponents.Builder other) { + addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs, + other.mPowerModels); + } + + public void addPowerAndDuration(PowerComponents other) { + addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs, + other.mPowerModels); + } + + private void addPowerAndDuration(double[] powerComponentsMah, + long[] usageDurationsMs, byte[] powerModels) { + if (mPowerComponentsMah.length != powerComponentsMah.length) { + throw new IllegalArgumentException( + "Number of power components does not match: " + powerComponentsMah.length + + ", expected: " + mPowerComponentsMah.length); + } + for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) { - mPowerComponentsMah[i] += other.mPowerComponentsMah[i]; + mPowerComponentsMah[i] += powerComponentsMah[i]; } for (int i = mUsageDurationsMs.length - 1; i >= 0; i--) { - mUsageDurationsMs[i] += other.mUsageDurationsMs[i]; + mUsageDurationsMs[i] += usageDurationsMs[i]; + } + if (mPowerModels != null && powerModels != null) { + for (int i = mPowerModels.length - 1; i >= 0; i--) { + if (mPowerModels[i] == POWER_MODEL_UNINITIALIZED) { + mPowerModels[i] = powerModels[i]; + } else if (mPowerModels[i] != powerModels[i] + && powerModels[i] != POWER_MODEL_UNINITIALIZED) { + mPowerModels[i] = BatteryConsumer.POWER_MODEL_UNDEFINED; + } + } } } @@ -433,6 +593,19 @@ class PowerComponents { return totalPowerMah; } + private byte[] getPowerModels() { + if (mPowerModels == null) { + return null; + } + + byte[] powerModels = new byte[mPowerModels.length]; + for (int i = mPowerModels.length - 1; i >= 0; i--) { + powerModels[i] = mPowerModels[i] != POWER_MODEL_UNINITIALIZED ? mPowerModels[i] + : BatteryConsumer.POWER_MODEL_UNDEFINED; + } + return powerModels; + } + /** * Creates a read-only object out of the Builder values. */ diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java index c33603d26ded..04165564ce1e 100644 --- a/core/java/android/os/SystemVibratorManager.java +++ b/core/java/android/os/SystemVibratorManager.java @@ -146,7 +146,7 @@ public class SystemVibratorManager extends VibratorManager { @Override public void cancel() { - cancelVibration(/* usageFilter= */ -1); + cancelVibration(VibrationAttributes.USAGE_FILTER_MATCH_ALL); } @Override diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index 16a6c767da38..bfc4f73835d9 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -19,9 +19,16 @@ package android.os; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.os.PowerCalculator; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -143,13 +150,65 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela return 0; } + /** Serializes this object to XML */ + void writeToXml(TypedXmlSerializer serializer) throws IOException { + if (getConsumedPower() == 0) { + return; + } + + serializer.startTag(null, BatteryUsageStats.XML_TAG_UID); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_UID, getUid()); + if (!TextUtils.isEmpty(mPackageWithHighestDrain)) { + serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE, + mPackageWithHighestDrain); + } + serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND, + mTimeInForegroundMs); + serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND, + mTimeInBackgroundMs); + mPowerComponents.writeToXml(serializer); + serializer.endTag(null, BatteryUsageStats.XML_TAG_UID); + } + + /** Parses an XML representation and populates the BatteryUsageStats builder */ + static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder) + throws XmlPullParserException, IOException { + final int uid = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_UID); + final UidBatteryConsumer.Builder consumerBuilder = + builder.getOrCreateUidBatteryConsumerBuilder(uid); + + int eventType = parser.getEventType(); + if (eventType != XmlPullParser.START_TAG + || !parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) { + throw new XmlPullParserException("Invalid XML parser state"); + } + + consumerBuilder.setPackageWithHighestDrain( + parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE)); + consumerBuilder.setTimeInStateMs(STATE_FOREGROUND, + parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND)); + consumerBuilder.setTimeInStateMs(STATE_BACKGROUND, + parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND)); + while (!(eventType == XmlPullParser.END_TAG + && parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) + && eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { + PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder); + } + } + eventType = parser.next(); + } + } + /** * Builder for UidBatteryConsumer. */ public static final class Builder extends BaseBuilder<Builder> { + private static final String PACKAGE_NAME_UNINITIALIZED = ""; private final BatteryStats.Uid mBatteryStatsUid; private final int mUid; - private String mPackageWithHighestDrain; + private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED; public long mTimeInForegroundMs; public long mTimeInBackgroundMs; private boolean mExcludeFromBatteryUsageStats; @@ -161,8 +220,19 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela mUid = batteryStatsUid.getUid(); } + public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels, + int uid) { + super(customPowerComponentNames, includePowerModels); + mBatteryStatsUid = null; + mUid = uid; + } + @NonNull public BatteryStats.Uid getBatteryStatsUid() { + if (mBatteryStatsUid == null) { + throw new IllegalStateException( + "UidBatteryConsumer.Builder was initialized without a BatteryStats.Uid"); + } return mBatteryStatsUid; } @@ -176,7 +246,7 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela */ @NonNull public Builder setPackageWithHighestDrain(@Nullable String packageName) { - mPackageWithHighestDrain = packageName; + mPackageWithHighestDrain = TextUtils.nullIfEmpty(packageName); return this; } @@ -208,6 +278,30 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela } /** + * Adds power and usage duration from the supplied UidBatteryConsumer. + */ + public Builder add(UidBatteryConsumer consumer) { + mPowerComponentsBuilder.addPowerAndDuration(consumer.mPowerComponents); + mTimeInBackgroundMs += consumer.mTimeInBackgroundMs; + mTimeInForegroundMs += consumer.mTimeInForegroundMs; + + if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) { + mPackageWithHighestDrain = consumer.mPackageWithHighestDrain; + } else if (!TextUtils.equals(mPackageWithHighestDrain, + consumer.mPackageWithHighestDrain)) { + // Consider combining two UidBatteryConsumers with this distribution + // of power drain between packages: + // (package1=100, package2=10) and (package1=100, package2=101). + // Since we don't know the actual power distribution between packages at this + // point, we have no way to correctly declare package1 as the winner. + // The naive logic of picking the consumer with the higher total consumed + // power would produce an incorrect result. + mPackageWithHighestDrain = null; + } + return this; + } + + /** * Returns true if this UidBatteryConsumer must be excluded from the * BatteryUsageStats. */ @@ -220,6 +314,9 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela */ @NonNull public UidBatteryConsumer build() { + if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) { + mPackageWithHighestDrain = null; + } return new UidBatteryConsumer(this); } } diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java index 429d2c53a836..b508a8cd98ae 100644 --- a/core/java/android/os/UserBatteryConsumer.java +++ b/core/java/android/os/UserBatteryConsumer.java @@ -17,9 +17,15 @@ package android.os; import android.annotation.NonNull; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.os.PowerCalculator; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -85,6 +91,42 @@ public class UserBatteryConsumer extends BatteryConsumer implements Parcelable { return 0; } + /** Serializes this object to XML */ + void writeToXml(TypedXmlSerializer serializer) throws IOException { + if (getConsumedPower() == 0) { + return; + } + + serializer.startTag(null, BatteryUsageStats.XML_TAG_USER); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID, getUserId()); + mPowerComponents.writeToXml(serializer); + serializer.endTag(null, BatteryUsageStats.XML_TAG_USER); + } + + /** Parses an XML representation and populates the BatteryUsageStats builder */ + static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder) + throws XmlPullParserException, IOException { + final int userId = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID); + final UserBatteryConsumer.Builder consumerBuilder = + builder.getOrCreateUserBatteryConsumerBuilder(userId); + + int eventType = parser.getEventType(); + if (eventType != XmlPullParser.START_TAG + || !parser.getName().equals(BatteryUsageStats.XML_TAG_USER)) { + throw new XmlPullParserException("Invalid XML parser state"); + } + while (!(eventType == XmlPullParser.END_TAG + && parser.getName().equals(BatteryUsageStats.XML_TAG_USER)) + && eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { + PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder); + } + } + eventType = parser.next(); + } + } + /** * Builder for UserBatteryConsumer. */ diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index c22224dd0da2..8709f071f222 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -26,6 +26,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.StringDef; import android.annotation.SuppressAutoDoc; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; @@ -53,6 +54,7 @@ import android.location.LocationManager; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.AndroidException; +import android.util.ArraySet; import android.view.WindowManager.LayoutParams; import com.android.internal.R; @@ -1322,6 +1324,24 @@ public class UserManager { "disallow_camera_toggle"; /** + * This is really not a user restriction in the normal sense. This can't be set to a user, + * via UserManager nor via DevicePolicyManager. This is not even set in UserSettingsUtils. + * This is defined here purely for convenience within the settings app. + * + * TODO(b/191306258): Refactor the Settings app to remove the need for this field, and delete it + * + * Specifies whether biometrics are available to the user. This is used internally only, + * as a means of communications between biometric settings and + * {@link com.android.settingslib.enterprise.ActionDisabledByAdminControllerFactory}. + * + * @see {@link android.hardware.biometrics.ParentalControlsUtilsInternal} + * @see {@link com.android.settings.biometrics.ParentalControlsUtils} + * + * @hide + */ + public static final String DISALLOW_BIOMETRIC = "disallow_biometric"; + + /** * Application restriction key that is used to indicate the pending arrival * of real restrictions for the app. * @@ -1415,6 +1435,7 @@ public class UserManager { DISALLOW_MICROPHONE_TOGGLE, DISALLOW_CAMERA_TOGGLE, KEY_RESTRICTIONS_PENDING, + DISALLOW_BIOMETRIC, }) @Retention(RetentionPolicy.SOURCE) public @interface UserRestrictionKey {} @@ -3202,6 +3223,33 @@ public class UserManager { } /** + * Returns the list of the system packages that would be installed on this type of user upon + * its creation. + * + * Returns {@code null} if all system packages would be installed. + * + * @hide + */ + @TestApi + @SuppressLint("NullableCollection") + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) + public @Nullable Set<String> getPreInstallableSystemPackages(@NonNull String userType) { + try { + final String[] installableSystemPackages + = mService.getPreInstallableSystemPackages(userType); + if (installableSystemPackages == null) { + return null; + } + return new ArraySet<>(installableSystemPackages); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * @hide * * Returns the preferred account name for user creation. diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java index cec323f8b423..43ea2e7810bc 100644 --- a/core/java/android/os/VibrationAttributes.java +++ b/core/java/android/os/VibrationAttributes.java @@ -63,6 +63,11 @@ public final class VibrationAttributes implements Parcelable { public @interface Usage{} /** + * Vibration usage filter value to match all usages. + * @hide + */ + public static final int USAGE_FILTER_MATCH_ALL = -1; + /** * Vibration usage class value to use when the vibration usage class is unknown. */ public static final int USAGE_CLASS_UNKNOWN = 0x0; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 36bc3e779c12..cb87653718c2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6661,6 +6661,20 @@ public final class Settings { public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category."; /** + * Whether or not compress blocks should be released on install. + * <p>The setting only determines if the platform will attempt to release + * compress blocks; it does not guarantee that the files will have their + * compress blocks released. Compression is currently only supported on + * some f2fs filesystems. + * <p> + * Type: int (0 for false, 1 for true) + * + * @hide + */ + public static final String RELEASE_COMPRESS_BLOCKS_ON_INSTALL = + "release_compress_blocks_on_install"; + + /** * List of input methods that are currently enabled. This is a string * containing the IDs of all enabled input methods, each ID separated * by ':'. diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 12d905588e1e..ff692818863a 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -50,6 +50,7 @@ import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.R; +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -162,6 +163,7 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_ENABLED = "enabled"; private static final String RULE_ATT_SNOOZING = "snoozing"; private static final String RULE_ATT_NAME = "name"; + private static final String RULE_ATT_PKG = "pkg"; private static final String RULE_ATT_COMPONENT = "component"; private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity"; private static final String RULE_ATT_ZEN = "zen"; @@ -671,11 +673,11 @@ public class ZenModeConfig implements Parcelable { rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY); - rt.pkg = (rt.component != null) - ? rt.component.getPackageName() - : (rt.configurationActivity != null) - ? rt.configurationActivity.getPackageName() - : null; + rt.pkg = XmlUtils.readStringAttribute(parser, RULE_ATT_PKG); + if (rt.pkg == null) { + // backfill from component, if present. configActivity is not safe to backfill from + rt.pkg = rt.component != null ? rt.component.getPackageName() : null; + } rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0); rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER); rt.condition = readConditionXml(parser); @@ -697,6 +699,9 @@ public class ZenModeConfig implements Parcelable { out.attribute(null, RULE_ATT_NAME, rule.name); } out.attributeInt(null, RULE_ATT_ZEN, rule.zenMode); + if (rule.pkg != null) { + out.attribute(null, RULE_ATT_PKG, rule.pkg); + } if (rule.component != null) { out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString()); } diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java index e1d4a5656b39..93c006aff435 100644 --- a/core/java/android/service/translation/TranslationService.java +++ b/core/java/android/service/translation/TranslationService.java @@ -361,6 +361,11 @@ public abstract class TranslationService extends Service { new Consumer<Set<TranslationCapability>>() { @Override public void accept(Set<TranslationCapability> values) { + if (!isValidCapabilities(sourceFormat, targetFormat, values)) { + throw new IllegalStateException("Invalid capabilities and " + + "format compatibility"); + } + final ArraySet<TranslationCapability> capabilities = new ArraySet<>(values); final Bundle bundle = new Bundle(); bundle.putParcelableArray(TranslationManager.EXTRA_CAPABILITIES, @@ -369,4 +374,23 @@ public abstract class TranslationService extends Service { } }); } + + /** + * Helper method to validate capabilities and format compatibility. + */ + private boolean isValidCapabilities(@TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat, Set<TranslationCapability> capabilities) { + if (sourceFormat != TranslationSpec.DATA_FORMAT_TEXT + && targetFormat != TranslationSpec.DATA_FORMAT_TEXT) { + return true; + } + + for (TranslationCapability capability : capabilities) { + if (capability.getState() == TranslationCapability.STATE_REMOVED_AND_AVAILABLE) { + return false; + } + } + + return true; + } } diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 67b97cee1b51..41374167cc56 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -263,6 +263,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { private static final int MSG_DETECTION_RESUME = 5; private static final int MSG_HOTWORD_REJECTED = 6; private static final int MSG_HOTWORD_STATUS_REPORTED = 7; + private static final int MSG_PROCESS_RESTARTED = 8; private final String mText; private final Locale mLocale; @@ -1212,6 +1213,12 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { message.arg1 = status; message.sendToTarget(); } + + @Override + public void onProcessRestarted() { + Slog.i(TAG, "onProcessRestarted"); + mHandler.sendEmptyMessage(MSG_PROCESS_RESTARTED); + } } class MyHandler extends Handler { @@ -1246,6 +1253,9 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { case MSG_HOTWORD_STATUS_REPORTED: mExternalCallback.onHotwordDetectionServiceInitialized(msg.arg1); break; + case MSG_PROCESS_RESTARTED: + mExternalCallback.onHotwordDetectionServiceRestarted(); + break; default: super.handleMessage(msg); } diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index b66d93d6316e..93a7ec793536 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -291,9 +291,7 @@ public abstract class HotwordDetectionService extends Service { @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory, @DurationMillisLong long callbackTimeoutMillis, - @Nullable IntConsumer statusCallback) { - // TODO: Handle the unimplemented case by throwing? - } + @Nullable IntConsumer statusCallback) {} /** * Called when the {@link VoiceInteractionService} requests that this service diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java index 204e7df89706..fb540b1622e6 100644 --- a/core/java/android/service/voice/SoftwareHotwordDetector.java +++ b/core/java/android/service/voice/SoftwareHotwordDetector.java @@ -122,7 +122,7 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { this.mCallback = callback; } - /** TODO: onDetected */ + /** Called when the detected result is valid. */ @Override public void onDetected( @Nullable HotwordDetectedResult hotwordDetectedResult, @@ -150,33 +150,45 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { public void onKeyphraseDetected( SoundTrigger.KeyphraseRecognitionEvent recognitionEvent, HotwordDetectedResult result) { - + if (DEBUG) { + Slog.i(TAG, "Ignored #onKeyphraseDetected event"); + } } @Override public void onGenericSoundTriggerDetected( SoundTrigger.GenericRecognitionEvent recognitionEvent) throws RemoteException { - + if (DEBUG) { + Slog.i(TAG, "Ignored #onGenericSoundTriggerDetected event"); + } } @Override public void onRejected(HotwordRejectedResult result) throws RemoteException { - + if (DEBUG) { + Slog.i(TAG, "Ignored #onRejected event"); + } } @Override public void onError(int status) throws RemoteException { - + if (DEBUG) { + Slog.i(TAG, "Ignored #onError (" + status + ") event"); + } } @Override public void onRecognitionPaused() throws RemoteException { - + if (DEBUG) { + Slog.i(TAG, "Ignored #onRecognitionPaused event"); + } } @Override public void onRecognitionResumed() throws RemoteException { - + if (DEBUG) { + Slog.i(TAG, "Ignored #onRecognitionResumed event"); + } } @Override @@ -187,6 +199,14 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { mCallback, status)); } + + @Override + public void onProcessRestarted() throws RemoteException { + Slog.v(TAG, "onProcessRestarted()"); + mHandler.sendMessage(obtainMessage( + HotwordDetector.Callback::onHotwordDetectionServiceRestarted, + mCallback)); + } } /** @hide */ diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 4f1354d7eee6..782a992d28e5 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -444,6 +444,13 @@ public final class InputDevice implements Parcelable { private static final int VIBRATOR_ID_ALL = -1; + /** + * The device id of input events generated inside accessibility service. + * @hide + */ + @TestApi + public static final int ACCESSIBILITY_DEVICE_ID = -2; + public static final @android.annotation.NonNull Parcelable.Creator<InputDevice> CREATOR = new Parcelable.Creator<InputDevice>() { public InputDevice createFromParcel(Parcel in) { diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 8080883c2b3e..145607ada4f4 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1265,7 +1265,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished(); } - if (invokeCallback && runningAnimation.startDispatched) { + if (invokeCallback) { dispatchAnimationEnd(runningAnimation.runner.getAnimation()); } break; diff --git a/core/java/android/view/ScrollCaptureTarget.java b/core/java/android/view/ScrollCaptureTarget.java index 44017ed0d831..a8bb037af5f9 100644 --- a/core/java/android/view/ScrollCaptureTarget.java +++ b/core/java/android/view/ScrollCaptureTarget.java @@ -21,13 +21,10 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiThread; -import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; -import com.android.internal.util.FastMath; - import java.io.PrintWriter; import java.util.function.Consumer; @@ -43,8 +40,7 @@ public final class ScrollCaptureTarget { private final int mHint; private Rect mScrollBounds; - private final float[] mTmpFloatArr = new float[2]; - private final Matrix mMatrixViewLocalToWindow = new Matrix(); + private final int[] mTmpIntArr = new int[2]; public ScrollCaptureTarget(@NonNull View scrollTarget, @NonNull Rect localVisibleRect, @NonNull Point positionInWindow, @NonNull ScrollCaptureCallback callback) { @@ -117,28 +113,15 @@ public final class ScrollCaptureTarget { } } - private static void zero(float[] pointArray) { - pointArray[0] = 0; - pointArray[1] = 0; - } - - private static void roundIntoPoint(Point pointObj, float[] pointArray) { - pointObj.x = FastMath.round(pointArray[0]); - pointObj.y = FastMath.round(pointArray[1]); - } - /** - * Refresh the local visible bounds and it's offset within the window, based on the current + * Refresh the local visible bounds and its offset within the window, based on the current * state of the {@code containing view}. */ @UiThread public void updatePositionInWindow() { - mMatrixViewLocalToWindow.reset(); - mContainingView.transformMatrixToGlobal(mMatrixViewLocalToWindow); - - zero(mTmpFloatArr); - mMatrixViewLocalToWindow.mapPoints(mTmpFloatArr); - roundIntoPoint(mPositionInWindow, mTmpFloatArr); + mContainingView.getLocationInWindow(mTmpIntArr); + mPositionInWindow.x = mTmpIntArr[0]; + mPositionInWindow.y = mTmpIntArr[1]; } public String toString() { diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index f34cd8f9de50..4e66ceb76a60 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -233,6 +233,7 @@ public final class SurfaceControl implements Parcelable { private static native void nativeRemoveJankDataListener(long nativeListener); private static native long nativeCreateJankDataListenerWrapper(OnJankDataListener listener); private static native int nativeGetGPUContextPriority(); + private static native void nativeSetTransformHint(long nativeObject, int transformHint); @Nullable @GuardedBy("mLock") @@ -348,6 +349,8 @@ public final class SurfaceControl implements Parcelable { @GuardedBy("mLock") private int mHeight; + private int mTransformHint; + private WeakReference<View> mLocalOwnerView; static GlobalTransactionWrapper sGlobalTransaction; @@ -605,6 +608,7 @@ public final class SurfaceControl implements Parcelable { mName = other.mName; mWidth = other.mWidth; mHeight = other.mHeight; + mTransformHint = other.mTransformHint; mLocalOwnerView = other.mLocalOwnerView; assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite); } @@ -1467,6 +1471,7 @@ public final class SurfaceControl implements Parcelable { mName = in.readString8(); mWidth = in.readInt(); mHeight = in.readInt(); + mTransformHint = in.readInt(); long object = 0; if (in.readInt() != 0) { @@ -1485,6 +1490,7 @@ public final class SurfaceControl implements Parcelable { dest.writeString8(mName); dest.writeInt(mWidth); dest.writeInt(mHeight); + dest.writeInt(mTransformHint); if (mNativeObject == 0) { dest.writeInt(0); } else { @@ -3602,4 +3608,27 @@ public final class SurfaceControl implements Parcelable { mHeight = h; nativeUpdateDefaultBufferSize(mNativeObject, w, h); } + + /** + * @hide + */ + public int getTransformHint() { + return mTransformHint; + } + + /** + * Update the transform hint of current SurfaceControl. Only affect if type is + * {@link #FX_SURFACE_BLAST} + * + * The transform hint is used to prevent allocating a buffer of different size when a + * layer is rotated. The producer can choose to consume the hint and allocate the buffer + * with the same size. + * @hide + */ + public void setTransformHint(@Surface.Rotation int transformHint) { + if (mTransformHint != transformHint) { + mTransformHint = transformHint; + nativeSetTransformHint(mNativeObject, transformHint); + } + } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 6b0bb9df5468..4f2cf6d9001e 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -30,7 +30,6 @@ import android.graphics.BLASTBufferQueue; import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.HardwareRenderer; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; @@ -214,6 +213,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) final Rect mSurfaceFrame = new Rect(); int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1; + int mTransformHint = 0; private boolean mGlobalListenersAdded; private boolean mAttachedToWindow; @@ -944,7 +944,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator, - boolean creating, boolean sizeChanged) { + boolean creating, boolean sizeChanged, boolean hintChanged) { boolean realSizeChanged = false; mSurfaceLock.lock(); @@ -1009,7 +1009,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius); - if (sizeChanged && !creating) { + if ((sizeChanged || hintChanged) && !creating) { setBufferSize(mTmpTransaction); } @@ -1081,17 +1081,18 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall || mWindowSpaceTop != mLocation[1]; final boolean layoutSizeChanged = getWidth() != mScreenRect.width() || getHeight() != mScreenRect.height(); - + final boolean hintChanged = viewRoot.getSurfaceTransformHint() != mTransformHint; if (creating || formatChanged || sizeChanged || visibleChanged || (mUseAlpha && alphaChanged) || windowVisibleChanged || - positionChanged || layoutSizeChanged) { + positionChanged || layoutSizeChanged || hintChanged) { getLocationInWindow(mLocation); if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged + " visible=" + visibleChanged + " alpha=" + alphaChanged + + " hint=" + hintChanged + " mUseAlpha=" + mUseAlpha + " visible=" + visibleChanged + " left=" + (mWindowSpaceLeft != mLocation[0]) @@ -1105,6 +1106,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mSurfaceHeight = myHeight; mFormat = mRequestedFormat; mLastWindowVisibility = mWindowVisibility; + mTransformHint = viewRoot.getSurfaceTransformHint(); mScreenRect.left = mWindowSpaceLeft; mScreenRect.top = mWindowSpaceTop; @@ -1130,9 +1132,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } final boolean realSizeChanged = performSurfaceTransaction(viewRoot, - translator, creating, sizeChanged); - final boolean redrawNeeded = sizeChanged || creating || - (mVisible && !mDrawFinished); + translator, creating, sizeChanged, hintChanged); + final boolean redrawNeeded = sizeChanged || creating || hintChanged + || (mVisible && !mDrawFinished); try { SurfaceHolder.Callback[] callbacks = null; @@ -1158,7 +1160,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall c.surfaceCreated(mSurfaceHolder); } } - if (creating || formatChanged || sizeChanged + if (creating || formatChanged || sizeChanged || hintChanged || visibleChanged || realSizeChanged) { if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "surfaceChanged -- format=" + mFormat @@ -1234,6 +1236,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void setBufferSize(Transaction transaction) { if (mUseBlastAdapter) { + mBlastSurfaceControl.setTransformHint(mTransformHint); mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat); } else { transaction.setBufferSize(mSurfaceControl, mSurfaceWidth, mSurfaceHeight); @@ -1330,6 +1333,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall if (mBlastBufferQueue != null) { mBlastBufferQueue.destroy(); } + mTransformHint = viewRoot.getSurfaceTransformHint(); + mBlastSurfaceControl.setTransformHint(mTransformHint); mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a8fe875f3485..9450801d8d83 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -20703,8 +20703,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Return the window this view is currently attached to. Used in - * {@link android.app.ActivityView} to communicate with WM. + * Return the window this view is currently attached to. * @hide */ protected IWindow getWindow() { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d42e0c367763..573ae998305e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -461,6 +461,9 @@ public final class ViewRootImpl implements ViewParent, protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo(); private final InputEventAssigner mInputEventAssigner = new InputEventAssigner(); + // Set to true if mSurfaceControl is used for Webview Overlay + private boolean mIsForWebviewOverlay; + /** * Update the Choreographer's FrameInfo object with the timing information for the current * ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next @@ -1374,6 +1377,23 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer.setASurfaceTransactionCallback(callback); } + /** + * Register a callback to be executed when Webview overlay needs a surface control. + * This callback will be executed on RenderThread worker thread, and released inside native code + * when CanvasContext is destroyed. + */ + private void addPrepareSurfaceControlForWebviewCallback() { + HardwareRenderer.PrepareSurfaceControlForWebviewCallback callback = () -> { + // make mSurfaceControl transparent, so child surface controls are visible + if (mIsForWebviewOverlay) return; + synchronized (ViewRootImpl.this) { + mIsForWebviewOverlay = true; + } + mTransaction.setOpaque(mSurfaceControl, false).apply(); + }; + mAttachInfo.mThreadedRenderer.setPrepareSurfaceControlForWebviewCallback(callback); + } + @UnsupportedAppUsage private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; @@ -1418,6 +1438,7 @@ public final class ViewRootImpl implements ViewParent, if (mHardwareRendererObserver != null) { mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); } + addPrepareSurfaceControlForWebviewCallback(); addASurfaceTransactionCallback(); mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl); } @@ -7743,6 +7764,7 @@ public final class ViewRootImpl implements ViewParent, } } if (mAttachInfo.mThreadedRenderer != null) { + addPrepareSurfaceControlForWebviewCallback(); addASurfaceTransactionCallback(); mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl); } @@ -7790,7 +7812,14 @@ public final class ViewRootImpl implements ViewParent, return; } - mTransaction.setOpaque(mSurfaceControl, opaque).apply(); + synchronized (this) { + if (mIsForWebviewOverlay) { + mIsSurfaceOpaque = false; + return; + } + mTransaction.setOpaque(mSurfaceControl, opaque).apply(); + } + mIsSurfaceOpaque = opaque; } @@ -10353,4 +10382,8 @@ public final class ViewRootImpl implements ViewParent, }); return true; } + + int getSurfaceTransformHint() { + return mSurfaceControl.getTransformHint(); + } } diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java index d8cd6056de90..27821fd6608d 100644 --- a/core/java/android/view/ViewRootInsetsControllerHost.java +++ b/core/java/android/view/ViewRootInsetsControllerHost.java @@ -110,6 +110,10 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host { @Override public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) { if (DEBUG) Log.d(TAG, "windowInsetsAnimation ended"); + if (mViewRoot.mView == null) { + // The view has already detached from window. + return; + } mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation); } diff --git a/core/java/android/view/WindowInsetsAnimation.java b/core/java/android/view/WindowInsetsAnimation.java index ab5b5ba51a6e..6576eea40496 100644 --- a/core/java/android/view/WindowInsetsAnimation.java +++ b/core/java/android/view/WindowInsetsAnimation.java @@ -360,6 +360,13 @@ public final class WindowInsetsAnimation { * finished, and then revert to the starting state of the animation in the first * {@link #onProgress} callback by using post-layout view properties like {@link View#setX} * and related methods. + * + * <p>Note that the animation might be cancelled before {@link #onStart} is dispatched. On + * {@link android.os.Build.VERSION_CODES#S S} and later, {@link #onEnd} is immediately + * dispatched without an {@link #onStart} in that case. + * On {@link android.os.Build.VERSION_CODES#R R}, no callbacks are dispatched after + * {@code #onPrepare} for such an animation. + * * <p> * Note: If the animation is application controlled by using * {@link WindowInsetsController#controlWindowInsetsAnimation}, the end state of the diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c1e394d7456a..5964f632da1a 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -2371,6 +2371,14 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000; /** + * Flag to prevent the window from being magnified by the accessibility magnifier. + * + * TODO(b/190623172): This is a temporary solution and need to find out another way instead. + * @hide + */ + public static final int PRIVATE_FLAG_NOT_MAGNIFIABLE = 0x00400000; + + /** * Flag to indicate that the status bar window is in a state such that it forces showing * the navigation bar unless the navigation bar window is explicitly set to * {@link View#GONE}. @@ -2473,6 +2481,7 @@ public interface WindowManager extends ViewManager { PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, + PRIVATE_FLAG_NOT_MAGNIFIABLE, PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC, PRIVATE_FLAG_USE_BLAST, @@ -2553,6 +2562,10 @@ public interface WindowManager extends ViewManager { equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, name = "IS_ROUNDED_CORNERS_OVERLAY"), @ViewDebug.FlagToString( + mask = PRIVATE_FLAG_NOT_MAGNIFIABLE, + equals = PRIVATE_FLAG_NOT_MAGNIFIABLE, + name = "NOT_MAGNIFIABLE"), + @ViewDebug.FlagToString( mask = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, equals = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, name = "STATUS_FORCE_SHOW_NAVIGATION"), diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 7668d80d3cb1..81c934dffa47 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -16,6 +16,9 @@ package android.view; +import static android.os.IInputConstants.POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY; +import static android.os.IInputConstants.POLICY_FLAG_INPUTFILTER_TRUSTED; + import android.annotation.IntDef; import android.os.PowerManager; @@ -27,10 +30,13 @@ import java.lang.annotation.RetentionPolicy; * @hide */ public interface WindowManagerPolicyConstants { - // Policy flags. These flags are also defined in frameworks/base/include/ui/Input.h. + // Policy flags. These flags are also defined in frameworks/base/include/ui/Input.h and + // frameworks/native/libs/input/android/os/IInputConstants.aidl int FLAG_WAKE = 0x00000001; int FLAG_VIRTUAL = 0x00000002; + int FLAG_INPUTFILTER_TRUSTED = POLICY_FLAG_INPUTFILTER_TRUSTED; + int FLAG_INJECTED_FROM_ACCESSIBILITY = POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY; int FLAG_INJECTED = 0x01000000; int FLAG_TRUSTED = 0x02000000; int FLAG_FILTERED = 0x04000000; diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 1b1dc4a709ca..10ae69118f54 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -16,6 +16,7 @@ package android.view.contentcapture; import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString; +import static android.view.contentcapture.ContentCaptureManager.DEBUG; import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID; import android.annotation.IntDef; @@ -509,11 +510,13 @@ public final class ContentCaptureEvent implements Parcelable { string.append(", class=").append(className); string.append(", id=").append(mNode.getAutofillId()); if (mNode.getText() != null) { - string.append(", text=").append(getSanitizedString(mNode.getText())); + string.append(", text=") + .append(DEBUG ? mNode.getText() : getSanitizedString(mNode.getText())); } } if (mText != null) { - string.append(", text=").append(getSanitizedString(mText)); + string.append(", text=") + .append(DEBUG ? mText : getSanitizedString(mText)); } if (mClientContext != null) { string.append(", context=").append(mClientContext); @@ -522,10 +525,13 @@ public final class ContentCaptureEvent implements Parcelable { string.append(", insets=").append(mInsets); } if (mComposingStart > MAX_INVALID_VALUE) { - string.append(", hasComposing"); + string.append(", composing=[") + .append(mComposingStart).append(",").append(mComposingEnd).append("]"); } if (mSelectionStartIndex > MAX_INVALID_VALUE) { - string.append(", hasSelection"); + string.append(", selection=[") + .append(mSelectionStartIndex).append(",") + .append(mSelectionEndIndex).append("]"); } return string.append(']').toString(); } diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index ed840ce3061b..9241c3074ddd 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -212,6 +212,9 @@ public final class ContentCaptureManager { private static final String TAG = ContentCaptureManager.class.getSimpleName(); + /** @hide */ + public static final boolean DEBUG = false; + /** Error happened during the data sharing session. */ public static final int DATA_SHARE_ERROR_UNKNOWN = 1; diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index c6332686bf7f..cc47f09d4e8d 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -341,11 +341,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { } } - try { - flush(FLUSH_REASON_SESSION_FINISHED); - } finally { - onDestroy(); - } + onDestroy(); } abstract void onDestroy(); diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index d8ac779ddc27..bcb914208958 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -263,7 +263,13 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @Override void onDestroy() { mHandler.removeMessages(MSG_FLUSH); - mHandler.post(() -> destroySession()); + mHandler.post(() -> { + try { + flush(FLUSH_REASON_SESSION_FINISHED); + } finally { + destroySession(); + } + }); } /** @@ -571,9 +577,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { private ParceledListSlice<ContentCaptureEvent> clearEvents() { // NOTE: we must save a reference to the current mEvents and then set it to to null, // otherwise clearing it would clear it in the receiving side if the service is also local. - final List<ContentCaptureEvent> events = mEvents == null - ? Collections.EMPTY_LIST - : new ArrayList<>(mEvents); + if (mEvents == null) { + return new ParceledListSlice<>(Collections.EMPTY_LIST); + } + + final List<ContentCaptureEvent> events = new ArrayList<>(mEvents); mEvents.clear(); return new ParceledListSlice<>(events); } diff --git a/core/java/android/view/translation/TranslationCapability.java b/core/java/android/view/translation/TranslationCapability.java index d104b2427c8e..65b749add1b2 100644 --- a/core/java/android/view/translation/TranslationCapability.java +++ b/core/java/android/view/translation/TranslationCapability.java @@ -61,6 +61,13 @@ public final class TranslationCapability implements Parcelable { * was dropped.</p> */ public static final @ModelState int STATE_NOT_AVAILABLE = 4; + /** + * The translation between the source and target specs were removed from the system, but is + * still available to be downloaded again. + * + * @hide + */ + public static final @ModelState int STATE_REMOVED_AND_AVAILABLE = 1000; /** * The state of translation readiness between {@code mSourceSpec} and {@code mTargetSpec}. @@ -134,7 +141,8 @@ public final class TranslationCapability implements Parcelable { STATE_AVAILABLE_TO_DOWNLOAD, STATE_DOWNLOADING, STATE_ON_DEVICE, - STATE_NOT_AVAILABLE + STATE_NOT_AVAILABLE, + STATE_REMOVED_AND_AVAILABLE }) @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -152,6 +160,8 @@ public final class TranslationCapability implements Parcelable { return "STATE_ON_DEVICE"; case STATE_NOT_AVAILABLE: return "STATE_NOT_AVAILABLE"; + case STATE_REMOVED_AND_AVAILABLE: + return "STATE_REMOVED_AND_AVAILABLE"; default: return Integer.toHexString(value); } } @@ -255,13 +265,15 @@ public final class TranslationCapability implements Parcelable { if (!(mState == STATE_AVAILABLE_TO_DOWNLOAD) && !(mState == STATE_DOWNLOADING) && !(mState == STATE_ON_DEVICE) - && !(mState == STATE_NOT_AVAILABLE)) { + && !(mState == STATE_NOT_AVAILABLE) + && !(mState == STATE_REMOVED_AND_AVAILABLE)) { throw new java.lang.IllegalArgumentException( "state was " + mState + " but must be one of: " + "STATE_AVAILABLE_TO_DOWNLOAD(" + STATE_AVAILABLE_TO_DOWNLOAD + "), " + "STATE_DOWNLOADING(" + STATE_DOWNLOADING + "), " + "STATE_ON_DEVICE(" + STATE_ON_DEVICE + "), " - + "STATE_NOT_AVAILABLE(" + STATE_NOT_AVAILABLE + ")"); + + "STATE_NOT_AVAILABLE(" + STATE_NOT_AVAILABLE + "), " + + "STATE_REMOVED_AND_AVAILABLE(" + STATE_REMOVED_AND_AVAILABLE + ")"); } this.mSourceSpec = sourceSpec; @@ -293,10 +305,10 @@ public final class TranslationCapability implements Parcelable { }; @DataClass.Generated( - time = 1621545303074L, + time = 1624307114468L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/translation/TranslationCapability.java", - inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)") + inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_REMOVED_AND_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS index 64570a8ad155..e1d601258b1d 100644 --- a/core/java/android/widget/OWNERS +++ b/core/java/android/widget/OWNERS @@ -8,6 +8,6 @@ siyamed@google.com mount@google.com njawad@google.com -per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com +per-file TextView*, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com per-file SpellChecker.java = file:../view/inputmethod/OWNERS diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl index 3eb35c2c5e8a..8b8dba89ea67 100644 --- a/core/java/android/window/ITaskOrganizer.aidl +++ b/core/java/android/window/ITaskOrganizer.aidl @@ -52,6 +52,11 @@ oneway interface ITaskOrganizer { void copySplashScreenView(int taskId); /** + * Called when the Task removed the splash screen. + */ + void onAppSplashScreenViewRemoved(int taskId); + + /** * A callback when the Task is available for the registered organizer. The client is responsible * for releasing the SurfaceControl in the callback. For non-root tasks, the leash may initially * be hidden so it is up to the organizer to show this task. diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index 6772afeb0270..4a3bf91645f2 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -34,8 +34,10 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteCallback; import android.os.Trace; import android.util.AttributeSet; import android.util.Log; @@ -76,7 +78,7 @@ import java.time.Instant; */ public final class SplashScreenView extends FrameLayout { private static final String TAG = SplashScreenView.class.getSimpleName(); - private static final boolean DEBUG = false; + private static final boolean DEBUG = Build.IS_DEBUGGABLE; private static final int LIGHT_BARS_MASK = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS @@ -85,6 +87,7 @@ public final class SplashScreenView extends FrameLayout { | FLAG_TRANSLUCENT_NAVIGATION | FLAG_TRANSLUCENT_STATUS; private boolean mNotCopyable; + private boolean mIsCopied; private int mInitBackgroundColor; private int mInitIconBackgroundColor; private View mIconView; @@ -103,6 +106,10 @@ public final class SplashScreenView extends FrameLayout { private SurfaceControlViewHost.SurfacePackage mSurfacePackage; @Nullable private SurfaceView mSurfaceView; + @Nullable + private SurfaceControlViewHost mSurfaceHost; + @Nullable + private RemoteCallback mClientCallback; // cache original window and status private Window mWindow; @@ -127,6 +134,7 @@ public final class SplashScreenView extends FrameLayout { private Bitmap mParceledIconBitmap; private Drawable mIconDrawable; private SurfaceControlViewHost.SurfacePackage mSurfacePackage; + private RemoteCallback mClientCallback; private int mBrandingImageWidth; private int mBrandingImageHeight; private Drawable mBrandingDrawable; @@ -161,6 +169,7 @@ public final class SplashScreenView extends FrameLayout { } mIconAnimationStart = Instant.ofEpochMilli(parcelable.mIconAnimationStartMillis); mIconAnimationDuration = Duration.ofMillis(parcelable.mIconAnimationDurationMillis); + mClientCallback = parcelable.mClientCallback; if (DEBUG) { Log.d(TAG, String.format("Building from parcel drawable: %s", mIconDrawable)); } @@ -228,6 +237,7 @@ public final class SplashScreenView extends FrameLayout { view.mInitBackgroundColor = mBackgroundColor; view.mInitIconBackgroundColor = mIconBackground; view.setBackgroundColor(mBackgroundColor); + view.mClientCallback = mClientCallback; view.mBrandingImageView = view.findViewById(R.id.splashscreen_branding_view); @@ -285,7 +295,8 @@ public final class SplashScreenView extends FrameLayout { if (mSurfacePackage == null) { if (DEBUG) { Log.d(TAG, - "Creating Original SurfacePackage. SurfaceView: " + surfaceView); + "SurfaceControlViewHost created on thread " + + Thread.currentThread().getId()); } SurfaceControlViewHost viewHost = new SurfaceControlViewHost(mContext, @@ -297,6 +308,7 @@ public final class SplashScreenView extends FrameLayout { SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage(); surfaceView.setChildSurfacePackage(surfacePackage); view.mSurfacePackage = surfacePackage; + view.mSurfaceHost = viewHost; view.mSurfacePackageCopy = new SurfaceControlViewHost.SurfacePackage( surfacePackage); } else { @@ -357,6 +369,7 @@ public final class SplashScreenView extends FrameLayout { * @hide */ public void onCopied() { + mIsCopied = true; if (mSurfaceView == null) { return; } @@ -369,6 +382,12 @@ public final class SplashScreenView extends FrameLayout { mSurfacePackage = null; } + /** @hide **/ + @Nullable + public SurfaceControlViewHost getSurfaceHost() { + return mSurfaceHost; + } + @Override public void setAlpha(float alpha) { super.setAlpha(alpha); @@ -407,8 +426,7 @@ public final class SplashScreenView extends FrameLayout { if (DEBUG) { mSurfacePackage.getSurfaceControl().addOnReparentListener( (transaction, parent) -> Log.e(TAG, - String.format("SurfacePackage'surface reparented.\n Parent: %s", - parent), new Throwable())); + String.format("SurfacePackage'surface reparented to %s", parent))); Log.d(TAG, "Transferring surface " + mSurfaceView.toString()); } mSurfaceView.setChildSurfacePackage(mSurfacePackage); @@ -466,9 +484,36 @@ public final class SplashScreenView extends FrameLayout { mHasRemoved = true; } + /** @hide **/ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + releaseAnimationSurfaceHost(); + } + + private void releaseAnimationSurfaceHost() { + if (mSurfaceHost != null && !mIsCopied) { + final SurfaceControlViewHost finalSurfaceHost = mSurfaceHost; + mSurfaceHost = null; + finalSurfaceHost.getView().post(() -> { + if (DEBUG) { + Log.d(TAG, + "Shell removed splash screen." + + " Releasing SurfaceControlViewHost on thread #" + + Thread.currentThread().getId()); + } + finalSurfaceHost.release(); + }); + } else if (mSurfacePackage != null && mSurfaceHost == null) { + mSurfacePackage = null; + mClientCallback.sendResult(null); + } + } + /** * Called when this view is attached to an activity. This also makes SystemUI colors * transparent so the content of splash screen view can draw fully. + * * @hide */ public void attachHostActivityAndSetSystemUIColors(Activity activity, Window window) { @@ -585,6 +630,7 @@ public final class SplashScreenView extends FrameLayout { private long mIconAnimationDurationMillis; private SurfaceControlViewHost.SurfacePackage mSurfacePackage; + private RemoteCallback mClientCallback; public SplashScreenViewParcelable(SplashScreenView view) { mIconSize = view.mIconView.getWidth(); @@ -641,6 +687,7 @@ public final class SplashScreenView extends FrameLayout { mIconAnimationDurationMillis = source.readLong(); mIconBackground = source.readInt(); mSurfacePackage = source.readTypedObject(SurfaceControlViewHost.SurfacePackage.CREATOR); + mClientCallback = source.readTypedObject(RemoteCallback.CREATOR); } @Override @@ -660,6 +707,7 @@ public final class SplashScreenView extends FrameLayout { dest.writeLong(mIconAnimationDurationMillis); dest.writeInt(mIconBackground); dest.writeTypedObject(mSurfacePackage, flags); + dest.writeTypedObject(mClientCallback, flags); } public static final @NonNull Parcelable.Creator<SplashScreenViewParcelable> CREATOR = @@ -697,5 +745,13 @@ public final class SplashScreenView extends FrameLayout { int getIconBackground() { return mIconBackground; } + + /** + * Sets the {@link RemoteCallback} that will be called by the client to notify the shell + * of the removal of the {@link SplashScreenView}. + */ + public void setClientCallback(@NonNull RemoteCallback clientCallback) { + mClientCallback = clientCallback; + } } } diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 3340cf4fb707..73995491668a 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -117,6 +117,16 @@ public class TaskOrganizer extends WindowOrganizer { public void copySplashScreenView(int taskId) {} /** + * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has + * removed the splash screen view. + * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int) + * @see SplashScreenView#remove() + */ + @BinderThread + public void onAppSplashScreenViewRemoved(int taskId) { + } + + /** * Called when a task with the registered windowing mode can be controlled by this task * organizer. For non-root tasks, the leash may initially be hidden so it is up to the organizer * to show this task. @@ -236,11 +246,16 @@ public class TaskOrganizer extends WindowOrganizer { } @Override - public void copySplashScreenView(int taskId) { + public void copySplashScreenView(int taskId) { mExecutor.execute(() -> TaskOrganizer.this.copySplashScreenView(taskId)); } @Override + public void onAppSplashScreenViewRemoved(int taskId) { + mExecutor.execute(() -> TaskOrganizer.this.onAppSplashScreenViewRemoved(taskId)); + } + + @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash)); } diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java index a600a948222a..c57afbc67494 100644 --- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java +++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java @@ -16,6 +16,7 @@ package com.android.internal.accessibility.util; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; @@ -28,6 +29,7 @@ import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__UNKNOWN; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON_LONG_PRESS; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY; @@ -37,6 +39,8 @@ import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_RE import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW; import android.content.ComponentName; +import android.content.Context; +import android.provider.Settings; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.ShortcutType; @@ -50,50 +54,54 @@ public final class AccessibilityStatsLogUtils { private AccessibilityStatsLogUtils() {} /** - * Logs accessibility feature name that is assigned to the shortcut also its shortcut type. + * Logs accessibility feature name that is assigned to the given {@code shortcutType}. * Calls this when clicking the shortcut {@link AccessibilityManager#ACCESSIBILITY_BUTTON} or - * {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} + * {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY}. * + * @param context context used to retrieve the {@link Settings} provider * @param componentName component name of the accessibility feature - * @param shortcutType accessibility shortcut type {@link ShortcutType} + * @param shortcutType accessibility shortcut type */ - public static void logAccessibilityShortcutActivated(ComponentName componentName, - @ShortcutType int shortcutType) { - logAccessibilityShortcutActivated(componentName, shortcutType, UNKNOWN_STATUS); + public static void logAccessibilityShortcutActivated(Context context, + ComponentName componentName, @ShortcutType int shortcutType) { + logAccessibilityShortcutActivatedInternal(componentName, + convertToLoggingShortcutType(context, shortcutType), UNKNOWN_STATUS); } /** - * Logs accessibility feature name that is assigned to the shortcut also its shortcut type and - * enabled status. Calls this when clicking the shortcut - * {@link AccessibilityManager#ACCESSIBILITY_BUTTON} - * or {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} + * Logs accessibility feature name that is assigned to the given {@code shortcutType} and the + * {@code serviceEnabled} status. + * Calls this when clicking the shortcut {@link AccessibilityManager#ACCESSIBILITY_BUTTON} + * or {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY}. * + * @param context context used to retrieve the {@link Settings} provider * @param componentName component name of the accessibility feature * @param shortcutType accessibility shortcut type * @param serviceEnabled {@code true} if the service is enabled */ - public static void logAccessibilityShortcutActivated(ComponentName componentName, - @ShortcutType int shortcutType, boolean serviceEnabled) { - logAccessibilityShortcutActivated(componentName, shortcutType, + public static void logAccessibilityShortcutActivated(Context context, + ComponentName componentName, @ShortcutType int shortcutType, boolean serviceEnabled) { + logAccessibilityShortcutActivatedInternal(componentName, + convertToLoggingShortcutType(context, shortcutType), convertToLoggingServiceStatus(serviceEnabled)); } /** - * Logs accessibility feature name that is assigned to the shortcut also its shortcut type and - * status code. Calls this when clicking the shortcut - * {@link AccessibilityManager#ACCESSIBILITY_BUTTON} - * or {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} + * Logs accessibility feature name that is assigned to the given {@code loggingShortcutType} and + * {@code loggingServiceStatus} code. * - * @param componentName component name of the accessibility feature - * @param shortcutType accessibility shortcut type {@link ShortcutType} - * @param serviceStatus The service status code. 0 denotes unknown_status, 1 denotes enabled, 2 - * denotes disabled. + * @param componentName component name of the accessibility feature + * @param loggingShortcutType accessibility shortcut type for logging. 0 denotes + * unknown_type, 1 denotes accessibility button, 2 denotes volume + * key, 3 denotes triple tap on the screen, 4 denotes long press on + * accessibility button, 5 denotes accessibility floating menu. + * @param loggingServiceStatus The service status code for logging. 0 denotes unknown_status, 1 + * denotes enabled, 2 denotes disabled. */ - private static void logAccessibilityShortcutActivated(ComponentName componentName, - @ShortcutType int shortcutType, int serviceStatus) { + private static void logAccessibilityShortcutActivatedInternal(ComponentName componentName, + int loggingShortcutType, int loggingServiceStatus) { FrameworkStatsLog.write(FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED, - componentName.flattenToString(), convertToLoggingShortcutType(shortcutType), - serviceStatus); + componentName.flattenToString(), loggingShortcutType, loggingServiceStatus); } /** @@ -144,10 +152,19 @@ public final class AccessibilityStatsLogUtils { convertToLoggingMagnificationMode(mode)); } - private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) { + private static boolean isFloatingMenuEnabled(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1) + == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; + } + + private static int convertToLoggingShortcutType(Context context, + @ShortcutType int shortcutType) { switch (shortcutType) { case ACCESSIBILITY_BUTTON: - return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; + return isFloatingMenuEnabled(context) + ? ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU + : ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; case ACCESSIBILITY_SHORTCUT_KEY: return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY; } diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl index ec99c95a737c..d0214e6e0082 100644 --- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl +++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl @@ -78,4 +78,7 @@ oneway interface IHotwordRecognitionStatusCallback { * @param status The status about the result of requesting update state action. */ void onStatusReported(int status); + + /** Called when the hotword detection process is restarted */ + void onProcessRestarted(); } diff --git a/core/java/com/android/internal/content/F2fsUtils.java b/core/java/com/android/internal/content/F2fsUtils.java new file mode 100644 index 000000000000..27f1b308ed9c --- /dev/null +++ b/core/java/com/android/internal/content/F2fsUtils.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.content; + +import android.annotation.NonNull; +import android.content.ContentResolver; +import android.os.Environment; +import android.os.incremental.IncrementalManager; +import android.provider.Settings.Secure; +import android.text.TextUtils; +import android.util.Slog; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility methods to work with the f2fs file system. + */ +public final class F2fsUtils { + private static final String TAG = "F2fsUtils"; + private static final boolean DEBUG_F2FS = false; + + /** Directory containing kernel features */ + private static final File sKernelFeatures = + new File("/sys/fs/f2fs/features"); + /** File containing features enabled on "/data" */ + private static final File sUserDataFeatures = + new File("/dev/sys/fs/by-name/userdata/features"); + private static final File sDataDirectory = Environment.getDataDirectory(); + /** Name of the compression feature */ + private static final String COMPRESSION_FEATURE = "compression"; + + private static final boolean sKernelCompressionAvailable; + private static final boolean sUserDataCompressionAvailable; + + static { + sKernelCompressionAvailable = isCompressionEnabledInKernel(); + if (!sKernelCompressionAvailable) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; feature not part of the kernel"); + } + } + sUserDataCompressionAvailable = isCompressionEnabledOnUserData(); + if (!sUserDataCompressionAvailable) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; feature not enabled on filesystem"); + } + } + } + + /** + * Releases compressed blocks from eligible installation artifacts. + * <p> + * Modern f2fs implementations starting in {@code S} support compression + * natively within the file system. The data blocks of specific installation + * artifacts [eg. .apk, .so, ...] can be compressed at the file system level, + * making them look and act like any other uncompressed file, but consuming + * a fraction of the space. + * <p> + * However, the unused space is not free'd automatically. Instead, we must + * manually tell the file system to release the extra blocks [the delta between + * the compressed and uncompressed block counts] back to the free pool. + * <p> + * Because of how compression works within the file system, once the blocks + * have been released, the file becomes read-only and cannot be modified until + * the free'd blocks have again been reserved from the free pool. + */ + public static void releaseCompressedBlocks(ContentResolver resolver, File file) { + if (!sKernelCompressionAvailable || !sUserDataCompressionAvailable) { + return; + } + + // NOTE: Retrieving this setting means we need to delay releasing cblocks + // of any APKs installed during the PackageManagerService constructor. Instead + // of being able to release them in the constructor, they can only be released + // immediately prior to the system being available. When we no longer need to + // read this setting, move cblock release back to the package manager constructor. + final boolean releaseCompressBlocks = + Secure.getInt(resolver, Secure.RELEASE_COMPRESS_BLOCKS_ON_INSTALL, 1) != 0; + if (!releaseCompressBlocks) { + if (DEBUG_F2FS) { + Slog.d(TAG, "SKIP; release compress blocks not enabled"); + } + return; + } + if (!isCompressionAllowed(file)) { + if (DEBUG_F2FS) { + Slog.d(TAG, "SKIP; compression not allowed"); + } + return; + } + final File[] files = getFilesToRelease(file); + if (files == null || files.length == 0) { + if (DEBUG_F2FS) { + Slog.d(TAG, "SKIP; no files to compress"); + } + return; + } + for (int i = files.length - 1; i >= 0; --i) { + final long releasedBlocks = nativeReleaseCompressedBlocks(files[i].getAbsolutePath()); + if (DEBUG_F2FS) { + Slog.d(TAG, "RELEASED " + releasedBlocks + " blocks" + + " from \"" + files[i] + "\""); + } + } + } + + /** + * Returns {@code true} if compression is allowed on the file system containing + * the given file. + * <p> + * NOTE: The return value does not mean if the given file, or any other file + * on the same file system, is actually compressed. It merely determines whether + * not files <em>may</em> be compressed. + */ + private static boolean isCompressionAllowed(@NonNull File file) { + final String filePath; + try { + filePath = file.getCanonicalPath(); + } catch (IOException e) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; could not determine path"); + } + return false; + } + if (IncrementalManager.isIncrementalPath(filePath)) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; file on incremental fs"); + } + return false; + } + if (!isChild(sDataDirectory, filePath)) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; file not on /data"); + } + return false; + } + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression ENABLED"); + } + return true; + } + + /** + * Returns {@code true} if the given child is a descendant of the base. + */ + private static boolean isChild(@NonNull File base, @NonNull String childPath) { + try { + base = base.getCanonicalFile(); + + File parentFile = new File(childPath).getCanonicalFile(); + while (parentFile != null) { + if (base.equals(parentFile)) { + return true; + } + parentFile = parentFile.getParentFile(); + } + return false; + } catch (IOException ignore) { + return false; + } + } + + /** + * Returns whether or not the compression feature is enabled in the kernel. + * <p> + * NOTE: This doesn't mean compression is enabled on a particular file system + * or any files have been compressed. Only that the functionality is enabled + * on the device. + */ + private static boolean isCompressionEnabledInKernel() { + final File[] features = sKernelFeatures.listFiles(); + if (features == null || features.length == 0) { + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; no kernel features"); + } + return false; + } + for (int i = features.length - 1; i >= 0; --i) { + final File feature = features[i]; + if (COMPRESSION_FEATURE.equals(features[i].getName())) { + if (DEBUG_F2FS) { + Slog.d(TAG, "FOUND kernel compression feature"); + } + return true; + } + } + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; kernel compression feature not found"); + } + return false; + } + + /** + * Returns whether or not the compression feature is enabled on user data [ie. "/data"]. + * <p> + * NOTE: This doesn't mean any files have been compressed. Only that the functionality + * is enabled on the file system. + */ + private static boolean isCompressionEnabledOnUserData() { + if (!sUserDataFeatures.exists() + || !sUserDataFeatures.isFile() + || !sUserDataFeatures.canRead()) { + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; filesystem features not available"); + } + return false; + } + final List<String> configLines; + try { + configLines = Files.readAllLines(sUserDataFeatures.toPath()); + } catch (IOException ignore) { + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; couldn't read filesystem features"); + } + return false; + } + if (configLines == null + || configLines.size() > 1 + || TextUtils.isEmpty(configLines.get(0))) { + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; no filesystem features"); + } + return false; + } + final String[] features = configLines.get(0).split(","); + for (int i = features.length - 1; i >= 0; --i) { + if (COMPRESSION_FEATURE.equals(features[i].trim())) { + if (DEBUG_F2FS) { + Slog.d(TAG, "FOUND filesystem compression feature"); + } + return true; + } + } + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; filesystem compression feature not found"); + } + return false; + } + + /** + * Returns all files contained within the directory at any depth from the given path. + */ + private static List<File> getFilesRecursive(@NonNull File path) { + final File[] allFiles = path.listFiles(); + if (allFiles == null) { + return null; + } + final ArrayList<File> files = new ArrayList<>(); + for (File f : allFiles) { + if (f.isDirectory()) { + files.addAll(getFilesRecursive(f)); + } else if (f.isFile()) { + files.add(f); + } + } + return files; + } + + /** + * Returns all files contained within the directory at any depth from the given path. + */ + private static File[] getFilesToRelease(@NonNull File codePath) { + final List<File> files = getFilesRecursive(codePath); + if (files == null) { + if (codePath.isFile()) { + return new File[] { codePath }; + } + return null; + } + if (files.size() == 0) { + return null; + } + return files.toArray(new File[files.size()]); + } + + private static native long nativeReleaseCompressedBlocks(String path); + +} diff --git a/core/java/com/android/internal/content/OWNERS b/core/java/com/android/internal/content/OWNERS new file mode 100644 index 000000000000..c42bee69410d --- /dev/null +++ b/core/java/com/android/internal/content/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 36137 +include /core/java/android/content/pm/OWNERS + +per-file ReferrerIntent.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file ReferrerIntent.java = file:/services/core/java/com/android/server/am/OWNERS diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java index b38f623e3a6d..3f3c9bdbe5f5 100644 --- a/core/java/com/android/internal/content/om/OverlayConfig.java +++ b/core/java/com/android/internal/content/om/OverlayConfig.java @@ -111,7 +111,7 @@ public class OverlayConfig { // Rebase the system partitions and settings file on the specified root directory. partitions = new ArrayList<>(PackagePartitions.getOrderedPartitions( p -> new OverlayPartition( - new File(rootDirectory, p.getFolder().getPath()), + new File(rootDirectory, p.getNonConicalFolder().getPath()), p))); } diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java index bd908900fccc..19183b8eb9e7 100644 --- a/core/java/com/android/internal/display/BrightnessSynchronizer.java +++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java @@ -31,9 +31,6 @@ import android.provider.Settings; import android.util.MathUtils; import android.view.Display; -import java.util.LinkedList; -import java.util.Queue; - /** * BrightnessSynchronizer helps convert between the int (old) system and float * (new) system for storing the brightness. It has methods to convert between the two and also @@ -43,12 +40,11 @@ public class BrightnessSynchronizer { private static final int MSG_UPDATE_FLOAT = 1; private static final int MSG_UPDATE_INT = 2; + private static final int MSG_UPDATE_BOTH = 3; private static final String TAG = "BrightnessSynchronizer"; private static final Uri BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); - private static final Uri BRIGHTNESS_FLOAT_URI = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT); // The tolerance within which we consider brightness values approximately equal to eachother. // This value is approximately 1/3 of the smallest possible brightness value. @@ -57,8 +53,6 @@ public class BrightnessSynchronizer { private DisplayManager mDisplayManager; private final Context mContext; - private final Queue<Object> mWriteHistory = new LinkedList<>(); - private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { @@ -69,6 +63,9 @@ public class BrightnessSynchronizer { case MSG_UPDATE_INT: updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1)); break; + case MSG_UPDATE_BOTH: + updateBoth(Float.intBitsToFloat(msg.arg1)); + break; default: super.handleMessage(msg); } @@ -139,7 +136,7 @@ public class BrightnessSynchronizer { /** * Translates specified value from the float brightness system to the int brightness system, - * given the min/max of each range. Accounts for special values such as OFF and invalid values. + * given the min/max of each range. Accounts for special values such as OFF and invalid values. * Value returned as a float primitive (to preserve precision), but is a value within the * int-system range. */ @@ -168,49 +165,63 @@ public class BrightnessSynchronizer { } /** - * Updates the float setting based on a passed in int value. This is called whenever the int - * setting changes. mWriteHistory keeps a record of the values that been written to the settings - * from either this method or updateBrightnessIntFromFloat. This is to ensure that the value - * being set is due to an external value being set, rather than the updateBrightness* methods. - * The intention of this is to avoid race conditions when the setting is being changed - * frequently and to ensure we are not reacting to settings changes from this file. + * Updates the settings based on a passed in int value. This is called whenever the int + * setting changes. mPreferredSettingValue holds the most recently updated brightness value + * as a float that we would like the display to be set to. + * + * We then schedule an update to both the int and float settings, but, remove all the other + * messages to update all, to prevent us getting stuck in a loop. + * * @param value Brightness value as int to store in the float setting. */ private void updateBrightnessFloatFromInt(int value) { - Object topOfQueue = mWriteHistory.peek(); - if (topOfQueue != null && topOfQueue.equals(value)) { - mWriteHistory.poll(); - } else { - if (brightnessFloatToInt(mPreferredSettingValue) == value) { - return; - } - float newBrightnessFloat = brightnessIntToFloat(value); - mWriteHistory.offer(newBrightnessFloat); - mPreferredSettingValue = newBrightnessFloat; - mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat); + if (brightnessFloatToInt(mPreferredSettingValue) == value) { + return; } + + mPreferredSettingValue = brightnessIntToFloat(value); + final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue); + mHandler.removeMessages(MSG_UPDATE_BOTH); + mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget(); } /** - * Updates the int setting based on a passed in float value. This is called whenever the float - * setting changes. mWriteHistory keeps a record of the values that been written to the settings - * from either this method or updateBrightnessFloatFromInt. This is to ensure that the value - * being set is due to an external value being set, rather than the updateBrightness* methods. - * The intention of this is to avoid race conditions when the setting is being changed - * frequently and to ensure we are not reacting to settings changes from this file. + * Updates the settings based on a passed in float value. This is called whenever the float + * setting changes. mPreferredSettingValue holds the most recently updated brightness value + * as a float that we would like the display to be set to. + * + * We then schedule an update to both the int and float settings, but, remove all the other + * messages to update all, to prevent us getting stuck in a loop. + * * @param value Brightness setting as float to store in int setting. */ private void updateBrightnessIntFromFloat(float value) { - int newBrightnessInt = brightnessFloatToInt(value); - Object topOfQueue = mWriteHistory.peek(); - if (topOfQueue != null && topOfQueue.equals(value)) { - mWriteHistory.poll(); - } else { - mWriteHistory.offer(newBrightnessInt); - mPreferredSettingValue = value; + if (floatEquals(mPreferredSettingValue, value)) { + return; + } + + mPreferredSettingValue = value; + final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue); + mHandler.removeMessages(MSG_UPDATE_BOTH); + mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget(); + } + + + /** + * Updates both setting values if they have changed + * mDisplayManager.setBrightness automatically checks for changes + * Settings.System.putIntForUser needs to be checked, to prevent an extra callback to this class + * + * @param newBrightnessFloat Brightness setting as float to store in both settings + */ + private void updateBoth(float newBrightnessFloat) { + int newBrightnessInt = brightnessFloatToInt(newBrightnessFloat); + mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat); + if (getScreenBrightnessInt(mContext) != newBrightnessInt) { Settings.System.putIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT); } + } /** diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java index af21f8183d3a..9ced6097804d 100644 --- a/core/java/com/android/internal/infra/ServiceConnector.java +++ b/core/java/com/android/internal/infra/ServiceConnector.java @@ -228,7 +228,7 @@ public interface ServiceConnector<I extends IInterface> { private final int mBindingFlags; private final @Nullable Function<IBinder, I> mBinderAsInterface; private final @NonNull Handler mHandler; - private final @NonNull Executor mExecutor; + protected final @NonNull Executor mExecutor; private volatile I mService = null; private boolean mBinding = false; diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 26d6a0c74b08..aabcd7f82ac7 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -43,6 +43,10 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR; @@ -53,6 +57,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP; import android.annotation.IntDef; import android.annotation.NonNull; @@ -153,6 +158,11 @@ public class InteractionJankMonitor { public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27; public static final int CUJ_SETTINGS_PAGE_SCROLL = 28; public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29; + public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30; + public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31; + public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32; + public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33; + public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34; private static final int NO_STATSD_LOGGING = -1; @@ -191,6 +201,11 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, }; private static volatile InteractionJankMonitor sInstance; @@ -240,6 +255,11 @@ public class InteractionJankMonitor { CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET, CUJ_SETTINGS_PAGE_SCROLL, CUJ_LOCKSCREEN_UNLOCK_ANIMATION, + CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON, + CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER, + CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE, + CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON, + CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -578,6 +598,16 @@ public class InteractionJankMonitor { return "SETTINGS_PAGE_SCROLL"; case CUJ_LOCKSCREEN_UNLOCK_ANIMATION: return "LOCKSCREEN_UNLOCK_ANIMATION"; + case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON: + return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON"; + case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER: + return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER"; + case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE: + return "SHADE_APP_LAUNCH_FROM_QS_TILE"; + case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON: + return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON"; + case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP: + return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 5dfc5faead43..945a6ab11856 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -341,6 +341,19 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Listener for the battery stats reset. + */ + public interface BatteryResetListener { + + /** + * Callback invoked immediately prior to resetting battery stats. + */ + void prepareForBatteryStatsReset(); + } + + private BatteryResetListener mBatteryResetListener; + public interface BatteryCallback { public void batteryNeedsCpuUpdate(); public void batteryPowerChanged(boolean onBattery); @@ -10736,6 +10749,10 @@ public class BatteryStatsImpl extends BatteryStats { } } + PowerProfile getPowerProfile() { + return mPowerProfile; + } + /** * Starts tracking CPU time-in-state for threads of the system server process, * keeping a separate account of threads receiving incoming binder calls. @@ -11184,6 +11201,10 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeCounter.reset(false, elapsedRealtimeUs); } + public void setBatteryResetListener(BatteryResetListener batteryResetListener) { + mBatteryResetListener = batteryResetListener; + } + public void resetAllStatsCmdLocked() { final long mSecUptime = mClocks.uptimeMillis(); long uptimeUs = mSecUptime * 1000; @@ -11219,6 +11240,10 @@ public class BatteryStatsImpl extends BatteryStats { } private void resetAllStatsLocked(long uptimeMillis, long elapsedRealtimeMillis) { + if (mBatteryResetListener != null) { + mBatteryResetListener.prepareForBatteryStatsReset(); + } + final long uptimeUs = uptimeMillis * 1000; final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000; mStartCount = 0; diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 3aaccdd71844..8943db6c8a1e 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -38,14 +38,24 @@ import java.util.Map; public class BatteryUsageStatsProvider { private final Context mContext; private final BatteryStats mStats; + private final BatteryUsageStatsStore mBatteryUsageStatsStore; private final PowerProfile mPowerProfile; private final Object mLock = new Object(); private List<PowerCalculator> mPowerCalculators; public BatteryUsageStatsProvider(Context context, BatteryStats stats) { + this(context, stats, null); + } + + @VisibleForTesting + public BatteryUsageStatsProvider(Context context, BatteryStats stats, + BatteryUsageStatsStore batteryUsageStatsStore) { mContext = context; mStats = stats; - mPowerProfile = new PowerProfile(mContext); + mBatteryUsageStatsStore = batteryUsageStatsStore; + mPowerProfile = stats instanceof BatteryStatsImpl + ? ((BatteryStatsImpl) stats).getPowerProfile() + : new PowerProfile(context); } private List<PowerCalculator> getPowerCalculators() { @@ -126,6 +136,15 @@ public class BatteryUsageStatsProvider { private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query, long currentTimeMs) { + if (query.getToTimestamp() == 0) { + return getCurrentBatteryUsageStats(query, currentTimeMs); + } else { + return getAggregatedBatteryUsageStats(query); + } + } + + private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query, + long currentTimeMs) { final long realtimeUs = elapsedRealtime() * 1000; final long uptimeUs = uptimeMillis() * 1000; @@ -209,6 +228,25 @@ public class BatteryUsageStatsProvider { BatteryStats.STATS_SINCE_CHARGED) / 1000; } + private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryUsageStatsQuery query) { + final boolean includePowerModels = (query.getFlags() + & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; + + final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( + mStats.getCustomEnergyConsumerNames(), includePowerModels); + final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps(); + for (long timestamp : timestamps) { + if (timestamp > query.getFromTimestamp() && timestamp <= query.getToTimestamp()) { + final BatteryUsageStats snapshot = + mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp); + if (snapshot != null) { + builder.add(snapshot); + } + } + } + return builder.build(); + } + private long elapsedRealtime() { if (mStats instanceof BatteryStatsImpl) { return ((BatteryStatsImpl) mStats).mClocks.elapsedRealtime(); diff --git a/core/java/com/android/internal/os/BatteryUsageStatsStore.java b/core/java/com/android/internal/os/BatteryUsageStatsStore.java new file mode 100644 index 000000000000..5c976025d39d --- /dev/null +++ b/core/java/com/android/internal/os/BatteryUsageStatsStore.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.annotation.Nullable; +import android.content.Context; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.Handler; +import android.util.AtomicFile; +import android.util.LongArray; +import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import android.util.Xml; + +import com.android.internal.annotations.VisibleForTesting; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.charset.StandardCharsets; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; + +/** + * A storage mechanism for BatteryUsageStats snapshots. + */ +public class BatteryUsageStatsStore { + private static final String TAG = "BatteryUsageStatsStore"; + + private static final List<BatteryUsageStatsQuery> BATTERY_USAGE_STATS_QUERY = List.of( + new BatteryUsageStatsQuery.Builder() + .setMaxStatsAgeMs(0) + .includePowerModels() + .build()); + private static final String BATTERY_USAGE_STATS_DIR = "battery-usage-stats"; + private static final String SNAPSHOT_FILE_EXTENSION = ".bus"; + private static final String DIR_LOCK_FILENAME = ".lock"; + private static final String CONFIG_FILENAME = "config"; + private static final String BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY = + "BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP"; + private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 100 * 1024; + + private final Context mContext; + private final BatteryStatsImpl mBatteryStats; + private final File mStoreDir; + private final File mLockFile; + private final AtomicFile mConfigFile; + private final long mMaxStorageBytes; + private final Handler mHandler; + private final BatteryUsageStatsProvider mBatteryUsageStatsProvider; + + public BatteryUsageStatsStore(Context context, BatteryStatsImpl stats, File systemDir, + Handler handler) { + this(context, stats, systemDir, handler, MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES); + } + + @VisibleForTesting + public BatteryUsageStatsStore(Context context, BatteryStatsImpl batteryStats, File systemDir, + Handler handler, long maxStorageBytes) { + mContext = context; + mBatteryStats = batteryStats; + mStoreDir = new File(systemDir, BATTERY_USAGE_STATS_DIR); + mLockFile = new File(mStoreDir, DIR_LOCK_FILENAME); + mConfigFile = new AtomicFile(new File(mStoreDir, CONFIG_FILENAME)); + mHandler = handler; + mMaxStorageBytes = maxStorageBytes; + mBatteryStats.setBatteryResetListener(this::prepareForBatteryStatsReset); + mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(mContext, mBatteryStats); + } + + private void prepareForBatteryStatsReset() { + final List<BatteryUsageStats> stats = + mBatteryUsageStatsProvider.getBatteryUsageStats(BATTERY_USAGE_STATS_QUERY); + if (stats.isEmpty()) { + Slog.wtf(TAG, "No battery usage stats generated"); + return; + } + + mHandler.post(() -> storeBatteryUsageStats(stats.get(0))); + } + + private void storeBatteryUsageStats(BatteryUsageStats stats) { + try (FileLock lock = lockSnapshotDirectory()) { + if (!mStoreDir.exists()) { + if (!mStoreDir.mkdirs()) { + Slog.e(TAG, + "Could not create a directory for battery usage stats snapshots"); + return; + } + } + File file = makeSnapshotFilename(stats.getStatsEndTimestamp()); + try { + writeXmlFileLocked(stats, file); + } catch (Exception e) { + Slog.e(TAG, "Cannot save battery usage stats", e); + } + + removeOldSnapshotsLocked(); + } catch (IOException e) { + Slog.e(TAG, "Cannot lock battery usage stats directory", e); + } + } + + /** + * Returns the timestamps of the stored BatteryUsageStats snapshots. The timestamp corresponds + * to the time the snapshot was taken {@link BatteryUsageStats#getStatsEndTimestamp()}. + */ + public long[] listBatteryUsageStatsTimestamps() { + LongArray timestamps = new LongArray(100); + try (FileLock lock = lockSnapshotDirectory()) { + for (File file : mStoreDir.listFiles()) { + String fileName = file.getName(); + if (fileName.endsWith(SNAPSHOT_FILE_EXTENSION)) { + try { + String fileNameWithoutExtension = fileName.substring(0, + fileName.length() - SNAPSHOT_FILE_EXTENSION.length()); + timestamps.add(Long.parseLong(fileNameWithoutExtension)); + } catch (NumberFormatException e) { + Slog.wtf(TAG, "Invalid format of BatteryUsageStats snapshot file name: " + + fileName); + } + } + } + } catch (IOException e) { + Slog.e(TAG, "Cannot lock battery usage stats directory", e); + } + return timestamps.toArray(); + } + + /** + * Reads the specified snapshot of BatteryUsageStats. Returns null if the snapshot + * does not exist. + */ + @Nullable + public BatteryUsageStats loadBatteryUsageStats(long timestamp) { + try (FileLock lock = lockSnapshotDirectory()) { + File file = makeSnapshotFilename(timestamp); + try { + return readXmlFileLocked(file); + } catch (Exception e) { + Slog.e(TAG, "Cannot read battery usage stats", e); + } + } catch (IOException e) { + Slog.e(TAG, "Cannot lock battery usage stats directory", e); + } + return null; + } + + /** + * Saves the supplied timestamp of the BATTERY_USAGE_STATS_BEFORE_RESET statsd atom pull + * in persistent file. + */ + public void setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(long timestamp) { + Properties props = new Properties(); + try (FileLock lock = lockSnapshotDirectory()) { + try (InputStream in = mConfigFile.openRead()) { + props.load(in); + } catch (IOException e) { + Slog.e(TAG, "Cannot load config file " + mConfigFile, e); + } + props.put(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY, + String.valueOf(timestamp)); + FileOutputStream out = null; + try { + out = mConfigFile.startWrite(); + props.store(out, "Statsd atom pull timestamps"); + mConfigFile.finishWrite(out); + } catch (IOException e) { + mConfigFile.failWrite(out); + Slog.e(TAG, "Cannot save config file " + mConfigFile, e); + } + } catch (IOException e) { + Slog.e(TAG, "Cannot lock battery usage stats directory", e); + } + } + + /** + * Retrieves the previously saved timestamp of the last BATTERY_USAGE_STATS_BEFORE_RESET + * statsd atom pull. + */ + public long getLastBatteryUsageStatsBeforeResetAtomPullTimestamp() { + Properties props = new Properties(); + try (FileLock lock = lockSnapshotDirectory()) { + try (InputStream in = mConfigFile.openRead()) { + props.load(in); + } catch (IOException e) { + Slog.e(TAG, "Cannot load config file " + mConfigFile, e); + } + } catch (IOException e) { + Slog.e(TAG, "Cannot lock battery usage stats directory", e); + } + return Long.parseLong( + props.getProperty(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY, "0")); + } + + private FileLock lockSnapshotDirectory() throws IOException { + mLockFile.getParentFile().mkdirs(); + mLockFile.createNewFile(); + return FileChannel.open(mLockFile.toPath(), StandardOpenOption.WRITE).lock(); + } + + /** + * Creates a file name by formatting the timestamp as 19-digit zero-padded number. + * This ensures that sorted directory list follows the chronological order. + */ + private File makeSnapshotFilename(long statsEndTimestamp) { + return new File(mStoreDir, String.format(Locale.ENGLISH, "%019d", statsEndTimestamp) + + SNAPSHOT_FILE_EXTENSION); + } + + private void writeXmlFileLocked(BatteryUsageStats stats, File file) throws IOException { + try (OutputStream out = new FileOutputStream(file)) { + TypedXmlSerializer serializer = Xml.newBinarySerializer(); + serializer.setOutput(out, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + stats.writeXml(serializer); + serializer.endDocument(); + } + } + + private BatteryUsageStats readXmlFileLocked(File file) + throws IOException, XmlPullParserException { + try (InputStream in = new FileInputStream(file)) { + TypedXmlPullParser parser = Xml.newBinaryPullParser(); + parser.setInput(in, StandardCharsets.UTF_8.name()); + return BatteryUsageStats.createFromXml(parser); + } + } + + private void removeOldSnapshotsLocked() { + // Read the directory list into a _sorted_ map. The alphanumeric ordering + // corresponds to the historical order of snapshots because the file names + // are timestamps zero-padded to the same length. + long totalSize = 0; + TreeMap<File, Long> mFileSizes = new TreeMap<>(); + for (File file : mStoreDir.listFiles()) { + final long fileSize = file.length(); + totalSize += fileSize; + if (file.getName().endsWith(SNAPSHOT_FILE_EXTENSION)) { + mFileSizes.put(file, fileSize); + } + } + + while (totalSize > mMaxStorageBytes) { + final Map.Entry<File, Long> entry = mFileSizes.firstEntry(); + if (entry == null) { + break; + } + + File file = entry.getKey(); + if (!file.delete()) { + Slog.e(TAG, "Cannot delete battery usage stats " + file); + } + totalSize -= entry.getValue(); + mFileSizes.remove(file); + } + } +} diff --git a/core/java/com/android/internal/view/inline/InlineTooltipUi.java b/core/java/com/android/internal/view/inline/InlineTooltipUi.java index 5ec8b30d6a7b..25fa678d0507 100644 --- a/core/java/com/android/internal/view/inline/InlineTooltipUi.java +++ b/core/java/com/android/internal/view/inline/InlineTooltipUi.java @@ -213,6 +213,8 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable if (!mShowing) { params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + params.privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_NOT_MAGNIFIABLE; mContentContainer.addOnLayoutChangeListener(mAnchoredOnLayoutChangeListener); mWm.addView(mContentContainer, params); mShowing = true; diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java index 4460e4a0ea78..ce6af49eef0b 100644 --- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java +++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java @@ -40,6 +40,7 @@ import com.android.internal.R; @RemoteViews.RemoteView public class EmphasizedNotificationButton extends Button { private final RippleDrawable mRipple; + private final GradientDrawable mBackground; private boolean mPriority; public EmphasizedNotificationButton(Context context) { @@ -57,9 +58,10 @@ public class EmphasizedNotificationButton extends Button { public EmphasizedNotificationButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - DrawableWrapper background = (DrawableWrapper) getBackground().mutate(); - mRipple = (RippleDrawable) background.getDrawable(); + mRipple = (RippleDrawable) getBackground(); mRipple.mutate(); + DrawableWrapper inset = (DrawableWrapper) mRipple.getDrawable(0); + mBackground = (GradientDrawable) inset.getDrawable(); } @RemotableViewMethod @@ -70,8 +72,7 @@ public class EmphasizedNotificationButton extends Button { @RemotableViewMethod public void setButtonBackground(ColorStateList color) { - GradientDrawable inner = (GradientDrawable) mRipple.getDrawable(0); - inner.setColor(color); + mBackground.setColor(color); invalidate(); } diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index bd0de2984b7f..6976ace36c11 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -240,6 +240,7 @@ public class SystemConfig { private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>(); private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>(); + private final ArraySet<String> mAllowedPartnerApexes = new ArraySet<>(); /** * Map of system pre-defined, uniquely named actors; keys are namespace, @@ -410,6 +411,10 @@ public class SystemConfig { return mWhitelistedStagedInstallers; } + public Set<String> getAllowedPartnerApexes() { + return mAllowedPartnerApexes; + } + public ArraySet<String> getAppDataIsolationWhitelistedApps() { return mAppDataIsolationWhitelistedApps; } @@ -1212,6 +1217,21 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "allowed-partner-apex": { + // TODO(b/189274479): should this be allowOemPermissions instead? + if (allowAppConfigs) { + String pkgName = parser.getAttributeValue(null, "package"); + if (pkgName == null) { + Slog.w(TAG, "<" + name + "> without package in " + permFile + + " at " + parser.getPositionDescription()); + } else { + mAllowedPartnerApexes.add(pkgName); + } + } else { + logNotAllowedInPartition(name, permFile, parser); + } + XmlUtils.skipCurrentTag(parser); + } break; default: { Slog.w(TAG, "Tag " + name + " is unknown in " + permFile + " at " + parser.getPositionDescription()); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 68388d98dbb4..125182cab254 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -84,6 +84,7 @@ cc_library_shared { android: { srcs: [ "AndroidRuntime.cpp", + "com_android_internal_content_F2fsUtils.cpp", "com_android_internal_content_NativeLibraryHelper.cpp", "com_google_android_gles_jni_EGLImpl.cpp", "com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 3debb3e03483..443bfce7f050 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -189,6 +189,7 @@ extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_android_security_Scrypt(JNIEnv *env); +extern int register_com_android_internal_content_F2fsUtils(JNIEnv* env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env); extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env); @@ -1624,6 +1625,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_animation_PropertyValuesHolder), REG_JNI(register_android_security_Scrypt), + REG_JNI(register_com_android_internal_content_F2fsUtils), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), REG_JNI(register_com_android_internal_os_DmabufInfoReader), REG_JNI(register_com_android_internal_os_FuseAppLoop), diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index e47f18a943d9..365a18d174c9 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -566,8 +566,9 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, env->ReleaseStringChars(clientPackageName, reinterpret_cast<const jchar*>(rawClientName)); - sp<Camera> camera = - Camera::connect(cameraId, clientName, Camera::USE_CALLING_UID, Camera::USE_CALLING_PID); + int targetSdkVersion = android_get_application_target_sdk_version(); + sp<Camera> camera = Camera::connect(cameraId, clientName, Camera::USE_CALLING_UID, + Camera::USE_CALLING_PID, targetSdkVersion); if (camera == NULL) { return -EACCES; } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index d528428a0e83..e9e79dc30c20 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1775,6 +1775,16 @@ static jint nativeGetGPUContextPriority(JNIEnv* env, jclass clazz) { return static_cast<jint>(SurfaceComposerClient::getGPUContextPriority()); } +static void nativeSetTransformHint(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl, + jint transformHint) { + sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl*>(nativeSurfaceControl)); + if (surface == nullptr) { + return; + } + surface->setTransformHint( + ui::Transform::toRotationFlags(static_cast<ui::Rotation>(transformHint))); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod sSurfaceControlMethods[] = { @@ -1962,6 +1972,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeCreateJankDataListenerWrapper }, {"nativeGetGPUContextPriority", "()I", (void*)nativeGetGPUContextPriority }, + {"nativeSetTransformHint", "(JI)V", + (void*)nativeSetTransformHint }, // clang-format on }; diff --git a/core/jni/com_android_internal_content_F2fsUtils.cpp b/core/jni/com_android_internal_content_F2fsUtils.cpp new file mode 100644 index 000000000000..8b9d59c416a0 --- /dev/null +++ b/core/jni/com_android_internal_content_F2fsUtils.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "F2fsUtils" + +#include "core_jni_helpers.h" + +#include <nativehelper/ScopedUtfChars.h> +#include <nativehelper/jni_macros.h> + +#include <sys/ioctl.h> +#include <sys/types.h> + +#include <linux/f2fs.h> +#include <linux/fs.h> + +#include <android-base/unique_fd.h> + +#include <utils/Log.h> + +#include <errno.h> +#include <fcntl.h> + +#include <array> + +using namespace std::literals; + +namespace android { + +static jlong com_android_internal_content_F2fsUtils_nativeReleaseCompressedBlocks(JNIEnv *env, + jclass clazz, + jstring path) { + unsigned long long blkcnt; + int ret; + ScopedUtfChars filePath(env, path); + + android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC, 0)); + if (fd < 0) { + ALOGW("Failed to open file: %s (%d)\n", filePath.c_str(), errno); + return 0; + } + + long flags = 0; + ret = ioctl(fd, FS_IOC_GETFLAGS, &flags); + if (ret < 0) { + ALOGW("Failed to get flags for file: %s (%d)\n", filePath.c_str(), errno); + return 0; + } + if ((flags & FS_COMPR_FL) == 0) { + return 0; + } + + ret = ioctl(fd, F2FS_IOC_RELEASE_COMPRESS_BLOCKS, &blkcnt); + if (ret < 0) { + return -errno; + } + return blkcnt; +} + +static const std::array gMethods = { + MAKE_JNI_NATIVE_METHOD( + "nativeReleaseCompressedBlocks", "(Ljava/lang/String;)J", + com_android_internal_content_F2fsUtils_nativeReleaseCompressedBlocks), +}; + +int register_com_android_internal_content_F2fsUtils(JNIEnv *env) { + return RegisterMethodsOrDie(env, "com/android/internal/content/F2fsUtils", gMethods.data(), + gMethods.size()); +} + +}; // namespace android diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml index 63707ab7f5d6..29c51f2a33c9 100644 --- a/core/res/res/drawable/btn_notification_emphasized.xml +++ b/core/res/res/drawable/btn_notification_emphasized.xml @@ -15,13 +15,13 @@ ~ limitations under the License --> -<inset xmlns:android="http://schemas.android.com/apk/res/android" - android:insetLeft="@dimen/button_inset_horizontal_material" - android:insetTop="@dimen/button_inset_vertical_material" - android:insetRight="@dimen/button_inset_horizontal_material" - android:insetBottom="@dimen/button_inset_vertical_material"> - <ripple android:color="?attr/colorControlHighlight"> - <item> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> + <item> + <inset + android:insetLeft="@dimen/button_inset_horizontal_material" + android:insetTop="@dimen/button_inset_vertical_material" + android:insetRight="@dimen/button_inset_horizontal_material" + android:insetBottom="@dimen/button_inset_vertical_material"> <shape android:shape="rectangle"> <corners android:radius="@dimen/notification_action_button_radius" /> <padding android:left="12dp" @@ -30,6 +30,6 @@ android:bottom="@dimen/button_padding_vertical_material" /> <solid android:color="@color/white" /> </shape> - </item> - </ripple> -</inset> + </inset> + </item> +</ripple> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 85de02e4be95..cd893185b4f6 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukke is geregistreer nie."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor is tydelik gedeaktiveer."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gebruik vingerafdruk"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gebruik vingerafdruk of skermslot"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Teks na knipbord gekopieër."</string> <string name="copied" msgid="4675902854553014676">"Gekopieer"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het uit <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> geplak"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> van jou knipbord af geplak"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het van jou knipbord af geplak"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het teks geplak wat jy gekopieer het"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het \'n prent geplak wat jy gekopieer het"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het inhoud geplak wat jy gekopieer het"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Laat \'n program toe om te versoek dat pakkette uitgevee word."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"vra om batteryoptimerings te ignoreer"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Laat \'n program toe om toestemming te vra om batteryoptimerings vir daardie program ignoreer."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"navraag oor alle pakkette"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Laat \'n program toe om alle geïnstalleerde pakette te sien."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Klop twee keer vir zoembeheer"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kon nie legstuk byvoeg nie."</string> <string name="ime_action_go" msgid="5536744546326495436">"Gaan"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 18cc4c67f22f..b261c461cd5e 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"አንድ መተግበሪያ የጥቅሎች ስረዛን እንዲጠይቅ ይፈቅዳል።"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"የባትሪ ማትባቶችን ችላ ለማለት መጠየቅ"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"አንድ መተግበሪያ ለዚያ መተግበሪያ የባትሪ ማትባቶችን ችላ ለማለት እንዲጠይቅ ይፈቅድለታል።"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 1d7ce153d4e5..b38bf9f93d73 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -613,6 +613,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1538,10 +1540,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"للسماح لتطبيق ما بطلب حذف الحِزم."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"طلب تجاهل تحسينات البطارية"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"للسماح للتطبيق بطلب الإذن لتجاهل تحسينات البطارية في هذا التطبيق."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index b1bf980fcbb4..846fb2c31d1f 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"এপটোক পেকেজবোৰ মচাৰ অনুৰোধ কৰিবলৈ দিয়ে।"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"বেটাৰি অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ বিচাৰক"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো এপক সেই এপটোৰ বাবে বেটাৰি অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ অনুমতি বিচাৰিবলৈ দিয়ে।"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index af66b0bebfe2..6b4736f71696 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Barmaq izi qeydə alınmayıb."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor müvəqqəti deaktivdir."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Barmaq <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmaq izini istifadə edin"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Barmaq izi və ya ekran kilidindən istifadə edin"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Mətn panoya kopyalandı."</string> <string name="copied" msgid="4675902854553014676">"Kopyalandı"</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> tətbiqindən əlavə edilib"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> mübadilə buferinizdən əlavə edilib"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> datanı panodan əlavə edib"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız mətni əlavə etdi"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız şəkli əlavə etdi"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız kontenti əlavə etdi"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Tətbiqə paketlərin silinməsi sorğusunu göndərməyə icazə verir."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"batareya optimallaşdırmasını iqnor etmək üçün soruşun"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Tatareya optimallaşdırılmasını o tətbiq üçün iqnor edilməsinə icazə vermək məqsədilə soruşmağa tətbiqə icazə verilir."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"bütün paketlər üçün sorğu göndərin"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Tətbiqə bütün quraşdırılmış paketləri görmək icazəsi verir."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Zoom kontrolu üçün iki dəfə toxunun"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget əlavə edilə bilmədi."</string> <string name="ime_action_go" msgid="5536744546326495436">"Get"</string> @@ -1626,7 +1626,7 @@ <string name="wireless_display_route_description" msgid="8297563323032966831">"Simsiz ekran"</string> <string name="media_route_button_content_description" msgid="2299223698196869956">"İştirakçılar"</string> <string name="media_route_chooser_title" msgid="6646594924991269208">"Cihaza qoş"</string> - <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Ekranı cihaza yayımla"</string> + <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Ekran yayımı"</string> <string name="media_route_chooser_searching" msgid="6119673534251329535">"Cihazlar axtarılır..."</string> <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Ayarlar"</string> <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Əlaqəni kəsin"</string> @@ -1697,15 +1697,15 @@ <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"DEAKTİV"</string> <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> xidmətinin cihaza tam nəzarət etməsinə icazə verilsin?"</string> <string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"<xliff:g id="SERVICE">%1$s</xliff:g> aktiv olarsa, cihazınız data şifrələnməsini genişləndirmək üçün ekran kilidini istifadə etməyəcək."</string> - <string name="accessibility_service_warning_description" msgid="291674995220940133">"Tam nəzarət əlçatımlılıq ehtiyaclarınızı ödəyən bəzi tətbiqlər üçün uyğundur."</string> + <string name="accessibility_service_warning_description" msgid="291674995220940133">"Tam nəzarət icazəsi xüsusi imkanlara dair yardım edən tətbiqlərə lazımdır, digər tətbiqlərə lazım deyil."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Baxış və nəzarət ekranı"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Ekrandakı bütün kontenti oxuya və kontenti digər tətbiqlərin üzərində göstərə bilər."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Əməliyyatlara baxın və icra edin"</string> - <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"O, tətbiq və ya avadanlıq sensoru ilə interaktivliyinizi izləyir və əvəzinizdən tətbiqlərlə qarşılıqlı əlaqəyə girir."</string> + <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Tətbiq və sensorlarla əlaqələrinizi izləyib tətbiqlərə adınızdan əmrlər verə bilər."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"İcazə verin"</string> <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"İmtina edin"</string> <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Funksiyanı istifadə etmək üçün onun üzərinə toxunun:"</string> - <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Əlçatımlılıq düyməsi ilə istifadə edəcəyiniz funksiyaları seçin"</string> + <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Xüsusi imkanlar düyməsinin köməyilə işə salınacaq funksiyaları seçin"</string> <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Səs səviyyəsi düyməsinin qısayolu ilə istifadə edəcəyiniz funksiyaları seçin"</string> <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> deaktiv edilib"</string> <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Qısayolları redaktə edin"</string> @@ -1722,7 +1722,7 @@ <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Xüsusi imkanlar düyməsinə toxunanda istədiyiniz funksiyanı seçin:"</string> <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Əlçatımlılıq jesti (iki barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"</string> <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Əlçatımlılıq jesti (üç barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"</string> - <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funksiyalar arasında keçid etmək üçün əlçatımlılıq düyməsinə toxunub saxlayın."</string> + <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funksiyalar arasında keçid etmək üçün xüsusi imkanlar düyməsini basıb saxlayın."</string> <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Funksiyalar arasında keçid etmək üçün iki barmağınızla yuxarı sürüşdürüb saxlayın."</string> <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Funksiyalar arasında keçid etmək üçün üç barmağınızla yuxarı doğru sürüşdürüb saxlayın."</string> <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Böyütmə"</string> @@ -1992,7 +1992,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"İşarələyin: <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"Çıxarın"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"İşarələməyin: <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"Tətbiq infosu"</string> + <string name="app_info" msgid="6113278084877079851">"Tətbiq haqqında"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Demo başlayır…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"Cihaz sıfırlanır…"</string> @@ -2139,7 +2139,7 @@ <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Sürətli Ayarlar"</string> <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Yandırıb-söndürmə dialoqu"</string> <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Kilid Ekranı"</string> - <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Ekran şəkli"</string> + <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Skrinşot"</string> <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Ekranda Əlçatımlılıq Qısayolu"</string> <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ekranda Əlçatımlılıq Qısayolu Seçicisi"</string> <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Əlçatımlılıq Qısayolu"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 4cc46d9b4176..5fcd2df25a78 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -365,11 +365,11 @@ <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 učinak ili ometaju rad uređaja kada se primi poruka o hitnom slučaju 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 učinak ili ometaju funkcionisanje uređaja kada se primi poruka info servisa o hitnom slučaju."</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> @@ -604,6 +604,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1022,7 +1024,7 @@ <string name="text_copied" msgid="2531420577879738860">"Tekst je kopiran u privremenu memoriju."</string> <string name="copied" msgid="4675902854553014676">"Kopirano je"</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">"Sadržaj aplikacije <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepljen u privr. memoriju"</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> @@ -1283,7 +1285,7 @@ <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 učinak bio bolji, možete da otvorite samo jednu od ovih igara odjednom."</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> @@ -1395,7 +1397,7 @@ <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">"Učinak je smanjen. Da biste onemogući konzolu, proverite pokretački program."</string> + <string name="console_running_notification_message" msgid="7892751888125174039">"Performanse su smanjene. Da biste onemogući konzolu, proverite pokretački program."</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> @@ -1478,10 +1480,8 @@ <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> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 142373747c8d..1af3ddd3b7fc 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -607,6 +607,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1025,7 +1027,7 @@ <string name="text_copied" msgid="2531420577879738860">"Тэкст скапіраваны ў буфер абмену."</string> <string name="copied" msgid="4675902854553014676">"Скапіравана"</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_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> @@ -1498,10 +1500,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Дазваляе праграме запытваць выдаленне пакетаў."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"запытваць дазвол на ігнараванне аптымізацыі акумулятара"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Дазваляе праграме запытваць дазвол на ігнараванне аптымізацыі акумулятара для гэтай праграмы."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 92e1a86198cc..b3722abd908b 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Разрешава на приложението да заявява изтриване на пакети."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"искане за пренебрегване на оптимизациите на батерията"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Разрешава на дадено приложение да иска разрешение за пренебрегване на свързаните с него оптимизации на батерията."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 2a9ecf66d4e9..69ddbb4f1f46 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"একটি অ্যাপ্লিকেশানকে প্যাকেজগুলি মুছে দেওয়ার অনুরোধ জানাতে দেয়৷"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ব্যাটারি অপ্টিমাইজেশন উপেক্ষা করার জন্য অনুমতি চাওয়া"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো অ্যাপের জন্য ব্যাটারি অপ্টিমাইজেশন উপেক্ষা করতে সেটিকে অনুমতির চাওয়ার মঞ্জুরি দেয়৷"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index f811ac2f4b95..74da24a6bfc4 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -604,6 +604,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije prijavljen nijedan otisak prsta."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristi otisak prsta"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Koristi otisak prsta ili zaključavanje ekrana"</string> @@ -1478,10 +1480,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Omogućava aplikaciji da zatraži brisanje paketa."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"traži zanemarivanje optimizacije baterije"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Omogućava aplikaciji da traži odobrenje za zanemarivanje optimizacije baterije za tu aplikaciju."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"upit za sve pakete"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Omogućava aplikaciji da pregleda sve instalirane pakete."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dodirnite dvaput za kontrolu uvećanja"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Dodavanje vidžeta nije uspjelo."</string> <string name="ime_action_go" msgid="5536744546326495436">"Započni"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 19762bfc0de3..03e7ef2e904b 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No s\'ha registrat cap empremta digital."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes digitals."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor està desactivat temporalment."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilitza l\'empremta digital"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilitza l\'empremta digital o el bloqueig de pantalla"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Text copiat al Porta-retalls."</string> <string name="copied" msgid="4675902854553014676">"S\'ha copiat"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat dades de: <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> ha enganxat contingut del porta-retalls"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat dades del porta-retalls"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat text que has copiat"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat una imatge que has copiat"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat contingut que has copiat"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permet que una aplicació sol·liciti la supressió de paquets."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Demanar permís per ignorar les optimitzacions de bateria"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permet que una aplicació demani permís per ignorar les optimitzacions de bateria per a l\'aplicació."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar tots els paquets"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permet que una aplicació vegi els paquets instal·lats."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Piqueu dos cops per controlar el zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"No s\'ha pogut afegir el widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Ves"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 95b2b551d78c..bfd56343d674 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -607,6 +607,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nejsou zaregistrovány žádné otisky prstů."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasně deaktivován."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použít otisk prstu"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Použít otisk prstu nebo zámek obrazovky"</string> @@ -1498,10 +1500,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Umožňuje aplikaci požádat o smazání balíčků."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"požádat o ignorování optimalizace využití baterie"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Povoluje aplikaci požádat o oprávnění ignorovat optimalizaci využití baterie, která pro ni je nastavena."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"zjistit všechny balíčky"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Umožňuje aplikaci načíst všechny nainstalované balíčky."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Poklepáním můžete ovládat přiblížení"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget nelze přidat."</string> <string name="ime_action_go" msgid="5536744546326495436">"Přejít"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 79379f10f2c8..4231048bccea 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Der er ikke registreret nogen fingeraftryk."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykslæser."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidigt deaktiveret."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Fingeraftryk <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Brug fingeraftryk"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Brug fingeraftryk eller skærmlås"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Tillader, at en app anmoder om sletning af pakker."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"bede om at ignorere batterioptimeringer"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gør det muligt for en app at bede om tilladelse til at ignorere batterioptimeringer for den pågældende app."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"forespørg om alle pakker"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Giver en app tilladelse til at se alle installerede pakker."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tryk to gange for zoomkontrol"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget kunne ikke tilføjes."</string> <string name="ime_action_go" msgid="5536744546326495436">"Gå"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index b8727e879f83..7a75eea2181c 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Keine Fingerabdrücke erfasst."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Der Sensor ist vorübergehend deaktiviert."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Fingerabdruck verwenden"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Fingerabdruck oder Displaysperre verwenden"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Text in Zwischenablage kopiert."</string> <string name="copied" msgid="4675902854553014676">"Kopiert"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat etwas von <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> eingefügt"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aus der Zwischenablage eingefügt"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat Informationen aus der Zwischenablage eingefügt"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat einen von dir kopierten Text eingefügt"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat ein von dir kopiertes Bild eingefügt"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat den von dir kopierten Inhalt eingefügt"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Ermöglicht der App, das Löschen von Paketen anzufordern."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"fragen, ob Akku-Leistungsoptimierungen ignoriert werden können"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Erlaubt einer App, nach der Berechtigung zum Ignorieren der Akku-Leistungsoptimierungen zu fragen."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Alle Pakete abfragen"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ermöglicht der App, alle installierten Pakete zu sehen."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Für Zoomeinstellung zweimal berühren"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget konnte nicht hinzugefügt werden."</string> <string name="ime_action_go" msgid="5536744546326495436">"Los"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 2aa10e597c08..523cde133638 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Επιτρέπει σε μια εφαρμογή να ζητά διαγραφή πακέτων."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"αίτημα αγνόησης βελτιστοποιήσεων μπαταρίας"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Επιτρέπει σε μια εφαρμογή να ζητήσει άδεια για την αγνόηση βελτιστοποιήσεων της μπαταρίας για τη συγκεκριμένη εφαρμογή."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 0817cb5d4316..b2eaab053b46 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimisations"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Go"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 6e3b041f1c2c..0a257f429b76 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimisations"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Go"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 74973c4e57f1..5eca8a68a12f 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimisations"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Go"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 8fe3f42e1672..74d32d4a1075 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimisations"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Go"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 2d921235a55e..385608faaf28 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -601,6 +601,7 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> + <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor needs calibration"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string> @@ -1458,10 +1459,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimizations"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimizations for that app."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Go"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 9102b8981869..fafb766c0dba 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -407,7 +407,7 @@ <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Permite que la aplicación se inicie en cuanto el sistema haya finalizado la inicialización. Esto puede ocasionar que la tablet tarde más en inicializarse y que la aplicación ralentice el funcionamiento general de la tablet al estar en ejecución constante."</string> <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Permite que se active la app en cuanto el sistema haya terminado de iniciarse. Esto puede ocasionar que el dispositivo Android TV tarde más en arrancar y que la app, al estar en ejecución constante, ralentice el funcionamiento general."</string> <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Permite que la aplicación se inicie en cuanto el sistema haya finalizado la inicialización. Esto puede ocasionar que el dispositivo tarde más en inicializarse y que la aplicación ralentice el funcionamiento general del dispositivo al estar en ejecución constante."</string> - <string name="permlab_broadcastSticky" msgid="4552241916400572230">"enviar emisiones pegajosas"</string> + <string name="permlab_broadcastSticky" msgid="4552241916400572230">"enviar emisiones persistentes"</string> <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Permite que la aplicación envíe transmisiones persistentes que permanecen después de que finaliza la transmisión. Un uso excesivo podría ralentizar la tablet o hacer que funcione de manera inestable al forzarla a utilizar mucha memoria."</string> <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Permite que la app envíe transmisiones persistentes que permanecen después de que finaliza la emisión. Un uso excesivo podría ralentizar el dispositivo Android TV o forzarlo a utilizar demasiada memoria, lo que generaría un funcionamiento inestable."</string> <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Permite que la aplicación envíe transmisiones persistentes que permanecen después de que finaliza la transmisión. Un uso excesivo podría ralentizar el dispositivo o hacer que funcione de manera inestable al forzarlo a utilizar mucha memoria."</string> @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se registraron huellas digitales."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas dactilares."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Se inhabilitó temporalmente el sensor."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar bloqueo de huella dactilar o pantalla"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite que una aplicación solicite que se borren paquetes."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"solicitar permiso para ignorar las optimizaciones de la batería"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que una app solicite permiso para ignorar las optimizaciones de la batería."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Búsqueda de todos los paquetes"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que una app vea todos los paquetes que se instalaron."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Presiona dos veces para obtener el control del zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"No se pudo agregar el widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 07f805be51af..3468da5db15a 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se ha registrado ninguna huella digital."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor está inhabilitado en estos momentos."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar huella digital o bloqueo de pantalla"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Texto copiado al portapapeles."</string> <string name="copied" msgid="4675902854553014676">"Copiado"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado contenido de <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"Se ha pegado <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> desde el portapapeles"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado contenido desde el portapapeles"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado texto que has copiado"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado una imagen que has copiado"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado contenido que has copiado"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite a una aplicación solicitar la eliminación de paquetes."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"solicitar permiso para ignorar las optimizaciones de la batería"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que una aplicación solicite permiso para ignorar las optimizaciones de la batería."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos los paquetes"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que una aplicación vea todos los paquetes instalados."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Da dos toques para acceder al control de zoom."</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"No se ha podido añadir el widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 937161b88dfd..0a60d66cd2e2 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ühtegi sõrmejälge pole registreeritud."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Andur on ajutiselt keelatud."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Sõrmejälg <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sõrmejälje kasutamine"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Sõrmejälje või ekraaniluku kasutamine"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Võimaldab rakendusel taotleda pakettide kustutamist."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"küsida luba aku optimeerimise eiramiseks"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Lubab rakendusel küsida luba rakenduse aku optimeerimise eiramiseks."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"päringute esitamine kõikide pakettide kohta"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Võimaldab rakendusel näha kõiki installitud pakette."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Suumi kasutamiseks koputage kaks korda"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Vidinat ei saanud lisada."</string> <string name="ime_action_go" msgid="5536744546326495436">"Mine"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 316013434506..e36cc324aed9 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ez da erregistratu hatz-markarik."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sentsorea aldi baterako desgaitu da."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. hatza"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Erabili hatz-marka"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Erabili hatz-marka edo pantailaren blokeoa"</string> @@ -866,11 +868,11 @@ <string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Idatzi PIN kodea"</string> <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Idatzi PUKa eta PIN berria"</string> <string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"PUK kodea"</string> - <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"PIN berria"</string> + <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"PIN kode berria"</string> <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Sakatu pasahitza idazteko"</font></string> <string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"Idatzi desblokeatzeko pasahitza"</string> - <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Idatzi desblokeatzeko PIN kodea"</string> - <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"PIN kode okerra."</string> + <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Idatzi desblokeatzeko PINa"</string> + <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"PIN kodea okerra da."</string> <string name="keyguard_label_text" msgid="3841953694564168384">"Desblokeatzeko, sakatu Menua eta, ondoren, 0."</string> <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Larrialdietarako zenbakia"</string> <string name="lockscreen_carrier_default" msgid="6192313772955399160">"Ez dago zerbitzurik"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Testua arbelean kopiatu da."</string> <string name="copied" msgid="4675902854553014676">"Kopiatu da"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> aplikaziotik itsatsi da <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak arbeletik itsatsi du"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak arbeleko edukia itsatsi du"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak kopiatu duzun testua itsatsi du"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak kopiatu duzun irudia itsatsi du"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak kopiatu duzun edukia itsatsi du"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Paketeak ezabatzeko eskatzea baimentzen die aplikazioei."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"eskatu bateria-optimizazioei ez ikusi egitea"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Bateriaren optimizazioei ez ikusi egiteko baimena eskatzea baimentzen die aplikazioei."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Kontsultatu pakete guztiak"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Instalatutako pakete guztiak ikusteko baimena ematen dio aplikazioari."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Sakatu birritan zooma kontrolatzeko"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Ezin izan da widgeta gehitu."</string> <string name="ime_action_go" msgid="5536744546326495436">"Joan"</string> @@ -1649,14 +1649,14 @@ <item quantity="one">Saiatu berriro segundo bat igarotakoan.</item> </plurals> <string name="kg_pattern_instructions" msgid="8366024510502517748">"Marraztu eredua"</string> - <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Idatzi SIMaren PIN kodea"</string> + <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Idatzi SIMaren PINa"</string> <string name="kg_pin_instructions" msgid="7355933174673539021">"Idatzi PINa"</string> <string name="kg_password_instructions" msgid="7179782578809398050">"Idatzi pasahitza"</string> <string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIMa desgaitu egin da. Jarraitzeko, idatzi PUK kodea. Xehetasunak lortzeko, jarri operadorearekin harremanetan."</string> <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Idatzi erabili nahi duzun PIN kodea"</string> <string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Berretsi erabili nahi duzun PIN kodea"</string> <string name="kg_sim_unlock_progress_dialog_message" msgid="8871937892678885545">"SIM txartela desblokeatzen…"</string> - <string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"PIN okerra."</string> + <string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"PIN kodea okerra da."</string> <string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Idatzi 4 eta 8 zenbaki arteko PINa."</string> <string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK kodeak 8 zenbaki izan behar ditu."</string> <string name="kg_invalid_puk" msgid="4809502818518963344">"Idatzi berriro PUK kode zuzena. Hainbat saiakera oker eginez gero, betiko desgaituko da SIMa."</string> @@ -1729,7 +1729,7 @@ <string name="user_switched" msgid="7249833311585228097">"Erabiltzailea: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="user_switching_message" msgid="1912993630661332336">"\"<xliff:g id="NAME">%1$s</xliff:g>\" erabiltzailera aldatzen…"</string> <string name="user_logging_out_message" msgid="7216437629179710359">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzailearen saioa amaitzen…"</string> - <string name="owner_name" msgid="8713560351570795743">"\"Jabea\""</string> + <string name="owner_name" msgid="8713560351570795743">"Jabea"</string> <string name="error_message_title" msgid="4082495589294631966">"Errorea"</string> <string name="error_message_change_not_allowed" msgid="843159705042381454">"Administratzaileak ez du eman aldaketa egiteko baimena"</string> <string name="app_not_found" msgid="3429506115332341800">"Ez da ekintza gauza dezakeen aplikaziorik aurkitu"</string> @@ -1835,13 +1835,13 @@ <string name="reason_service_unavailable" msgid="5288405248063804713">"Inprimatze-zerbitzua ez dago gaituta"</string> <string name="print_service_installed_title" msgid="6134880817336942482">"<xliff:g id="NAME">%s</xliff:g> zerbitzua instalatu da"</string> <string name="print_service_installed_message" msgid="7005672469916968131">"Sakatu gaitzeko"</string> - <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Idatzi administratzailearen PIN kodea"</string> + <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Idatzi administratzailearen PINa"</string> <string name="restr_pin_enter_pin" msgid="373139384161304555">"Idatzi PINa"</string> <string name="restr_pin_incorrect" msgid="3861383632940852496">"Okerra"</string> <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Oraingo PINa"</string> <string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"PIN berria"</string> <string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Berretsi PIN berria"</string> - <string name="restr_pin_create_pin" msgid="917067613896366033">"Konfiguratu debekuak aldatu ahal izateko idatzi beharko den PIN kodea"</string> + <string name="restr_pin_create_pin" msgid="917067613896366033">"Konfiguratu debekuak aldatu ahal izateko idatzi beharko den PINa"</string> <string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"PINak ez datoz bat. Saiatu berriro."</string> <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PINa laburregia da. Lau digitu izan behar ditu gutxienez."</string> <plurals name="restr_pin_countdown" formatted="false" msgid="4427486903285216153"> @@ -2168,11 +2168,11 @@ <string name="miniresolver_open_in_work" msgid="152208044699347924">"Laneko profileko <xliff:g id="APP">%s</xliff:g> aplikazioan ireki nahi duzu?"</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Erabili arakatzaile pertsonala"</string> <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Erabili laneko arakatzailea"</string> - <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"SIMaren sarearen bidez desblokeatzeko PIN kodea"</string> + <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"SIMaren sarearen bidez desblokeatzeko PINa"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"SIMaren sareko azpimultzoaren bidez desblokeatzeko PINa"</string> - <string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY" msgid="4447629474818217364">"Enpresaren SIMaren bidez desblokeatzeko PIN kodea"</string> - <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY" msgid="973059024670737358">"SIMaren zerbitzu-hornitzailearen bidez desblokeatzeko PIN kodea"</string> - <string name="PERSOSUBSTATE_SIM_SIM_ENTRY" msgid="4487435301206073787">"SIMaren bidez desblokeatzeko PIN kodea"</string> + <string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY" msgid="4447629474818217364">"Enpresaren SIMaren bidez desblokeatzeko PINa"</string> + <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY" msgid="973059024670737358">"SIMaren zerbitzu-hornitzailearen bidez desblokeatzeko PINa"</string> + <string name="PERSOSUBSTATE_SIM_SIM_ENTRY" msgid="4487435301206073787">"SIMaren bidez desblokeatzeko PINa"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY" msgid="768060297218652809">"Idatzi PUK kodea"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY" msgid="7129527319490548930">"Idatzi PUK kodea"</string> <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ENTRY" msgid="2876126640607573252">"Idatzi PUK kodea"</string> @@ -2181,9 +2181,9 @@ <string name="PERSOSUBSTATE_RUIM_NETWORK1_ENTRY" msgid="2974411408893410289">"RUIMaren 1 motako sarearen bidez desblokeatzeko PINa"</string> <string name="PERSOSUBSTATE_RUIM_NETWORK2_ENTRY" msgid="687618528751880721">"RUIMaren 2 motako sarearen bidez desblokeatzeko PINa"</string> <string name="PERSOSUBSTATE_RUIM_HRPD_ENTRY" msgid="6810596579655575381">"HRPD sarearen bidez desblokeatzeko PINa"</string> - <string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY" msgid="2715929642540980259">"Enpresaren RUIMaren bidez desblokeatzeko PIN kodea"</string> - <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY" msgid="8557791623303951590">"RUIMaren zerbitzu-hornitzailearen bidez desblokeatzeko PIN kodea"</string> - <string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY" msgid="7382468767274580323">"RUIMaren bidez desblokeatzeko PIN kodea"</string> + <string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY" msgid="2715929642540980259">"Enpresaren RUIMaren bidez desblokeatzeko PINa"</string> + <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY" msgid="8557791623303951590">"RUIMaren zerbitzu-hornitzailearen bidez desblokeatzeko PINa"</string> + <string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY" msgid="7382468767274580323">"RUIMaren bidez desblokeatzeko PINa"</string> <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY" msgid="6730880791104286987">"Idatzi PUK kodea"</string> <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY" msgid="6432126539782267026">"Idatzi PUK kodea"</string> <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ENTRY" msgid="1730510161529488920">"Idatzi PUK kodea"</string> @@ -2191,8 +2191,8 @@ <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ENTRY" msgid="9129139686191167829">"Idatzi PUK kodea"</string> <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ENTRY" msgid="2869929685874615358">"Idatzi PUK kodea"</string> <string name="PERSOSUBSTATE_SIM_SPN_ENTRY" msgid="1238663472392741771">"SPNaren bidez desblokeatzeko PINa"</string> - <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY" msgid="3988705848553894358">"Zerbitzu-hornitzailearen PLMN sare nagusi baliokidearen bidez desblokeatzeko PIN kodea"</string> - <string name="PERSOSUBSTATE_SIM_ICCID_ENTRY" msgid="6186770686690993200">"ICCIDaren bidez desblokeatzeko PIN kodea"</string> + <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY" msgid="3988705848553894358">"Zerbitzu-hornitzailearen PLMN sare nagusi baliokidearen bidez desblokeatzeko PINa"</string> + <string name="PERSOSUBSTATE_SIM_ICCID_ENTRY" msgid="6186770686690993200">"ICCIDaren bidez desblokeatzeko PINa"</string> <string name="PERSOSUBSTATE_SIM_IMPI_ENTRY" msgid="7043865376145617024">"IMPIaren bidez desblokeatzeko PINa"</string> <string name="PERSOSUBSTATE_SIM_NS_SP_ENTRY" msgid="6144227308185112176">"Sareko azpimultzoaren zerbitzu-hornitzailearen bidez desblokeatzeko PINa"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_IN_PROGRESS" msgid="4233355366318061180">"SIMaren sarearen bidez desblokeatzeko eskatzen…"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 3787d4c9db5f..add623a593f9 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"به برنامه اجازه میدهد حذف بستهها را درخواست کند."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"درخواست نادیدهگرفتن بهینهسازی باتری"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"به یک برنامه اجازه میدهد جهت نادیده گرفتن بهینهسازی باتری برای خود مجوز درخواست کند."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 965970cc9e54..5b4caa7b71fe 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Sormenjälkiä ei ole otettu käyttöön."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tunnistin poistettu väliaikaisesti käytöstä."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Sormi <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Käytä sormenjälkeä"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Käytä sormenjälkeä tai näytön lukitusta"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Antaa sovelluksen pyytää pakettien poistamista."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Lupa ohittaa akun optimoinnit"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Sallii sovelluksen pyytää lupaa ohittaa tietyn sovelluksen akun optimoinnit."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kaikkien pakettien näkeminen"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Antaa sovelluksen nähdä kaikki asennetut paketit."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Hallitse zoomausta napauttamalla kahdesti"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widgetin lisääminen epäonnistui."</string> <string name="ime_action_go" msgid="5536744546326495436">"Siirry"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 44754d7a9bbf..29c9698479fa 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser l\'empreinte digitale ou le verrouillage de l\'écran"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permet à une application de demander la suppression de paquets."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"demander d\'ignorer les optimisations de la pile"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permet à une application de demander la permission d\'ignorer les optimisations de la pile."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"envoyer une requête à propos de tous les paquets"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permet à une application de voir tous les paquets installés."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Appuyer deux fois pour régler le zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Impossible d\'ajouter le widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Aller"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index cc22d83eb367..23e599e1d948 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Capteur temporairement désactivé."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser votre empreinte digitale ou le verrouillage de l\'écran"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Le texte a été copié dans le presse-papier."</string> <string name="copied" msgid="4675902854553014676">"Copie effectuée"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> collé depuis <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a copié des données depuis le presse-papiers"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé des données depuis le presse-papiers"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé du texte que vous avez copié"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé une image que vous avez copiée"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé le contenu que vous avez copié"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permet à une application de demander la suppression de packages."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"demander à ignorer les optimisations de batterie"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Autorise une application à demander l\'autorisation d\'ignorer les optimisations de batterie pour cette application."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"interroger tous les packages"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Autorise une appli à voir tous les packages installés."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Appuyer deux fois pour régler le zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Impossible d\'ajouter le widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"OK"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index ba41c75c514a..70f703a34648 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Non se rexistraron impresións dixitais."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Desactivouse o sensor temporalmente."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar impresión dixital"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilizar impresión dixital ou credencial do dispositivo"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite a unha aplicación solicitar a eliminación dos paquetes."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"pedir que se ignore a optimización da batería"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Fai que unha aplicación poida solicitar permiso para ignorar as optimizacións da batería."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os paquetes"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que unha aplicación consulte todos os paquetes instalados."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toca dúas veces para controlar o zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Non se puido engadir o widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 4157e1fd32eb..4d27b1086f61 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ઍપ્લિકેશનને પૅકેજો કાઢી નાખવાની વિનંતી કરવાની મંજૂરી આપે છે."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"બૅટરી ઓપ્ટિમાઇઝેશન્સને અવગણવા માટે પૂછો"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ઍપ્લિકેશનને તે ઍપ્લિકેશન માટે બૅટરી ઓપ્ટિમાઇઝેશન્સને અવગણવાની પરવાનગી આપવા માટે પૂછવાની મંજૂરી આપે છે."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 6e8bd94f3f1d..74f4f4612d24 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"किसी ऐप्लिकेशन को पैकेज हटाने का अनुरोध करने देती है."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"बैटरी ऑप्टिमाइज़ेशन पर ध्यान ना देने के लिए पूछें"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"किसी ऐप्लिकेशन को उस ऐप्लिकेशन के लिए बैटरी ऑप्टिमाइज़ेशन पर ध्यान ना देने की अनुमति के लिए पूछने देता है."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 98acb8993dbe..13a7c450f6d3 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -604,6 +604,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registriran nijedan otisak prsta."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Upotreba otiska prsta"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Upotreba otiska prsta ili zaključavanja zaslona"</string> @@ -1022,7 +1024,7 @@ <string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međuspremnik."</string> <string name="copied" msgid="4675902854553014676">"Kopirano"</string> <string name="pasted_from_app" msgid="5627698450808256545">"U aplikaciji <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepljen je sadržaj aplikacije <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"Zalijepljen je sadržaj aplikacije <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"Apl. <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepila je ovaj sadržaj iz međuspremnika"</string> <string name="pasted_text" msgid="4298871641549173733">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepila je tekst koji ste kopirali"</string> <string name="pasted_image" msgid="4729097394781491022">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepila je sliku koju ste kopirali"</string> <string name="pasted_content" msgid="646276353060777131">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepila je ono što ste kopirali"</string> @@ -1212,7 +1214,7 @@ <string name="whichEditApplication" msgid="6191568491456092812">"Uređivanje pomoću aplikacije"</string> <string name="whichEditApplicationNamed" msgid="8096494987978521514">"Uređivanje pomoću aplikacije %1$s"</string> <string name="whichEditApplicationLabel" msgid="1463288652070140285">"Uredi"</string> - <string name="whichSendApplication" msgid="4143847974460792029">"Dijeli"</string> + <string name="whichSendApplication" msgid="4143847974460792029">"Dijeljenje"</string> <string name="whichSendApplicationNamed" msgid="4470386782693183461">"Dijeljenje pomoću aplikacije %1$s"</string> <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Dijeli"</string> <string name="whichSendToApplication" msgid="77101541959464018">"Pošalji aplikacijom"</string> @@ -1478,10 +1480,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Aplikaciji omogućuje zahtijevanje brisanja paketa."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"tražiti zanemarivanje optimizacija baterije"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Aplikaciji omogućuje da traži dopuštenje za zanemarivanje optimizacija baterije za tu aplikaciju."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"slanje upita za sve pakete"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Aplikaciji omogućuje pregled instaliranih paketa."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dvaput dotaknite za upravljanje zumiranjem"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget nije moguće dodati."</string> <string name="ime_action_go" msgid="5536744546326495436">"Idi"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 58c38755cf6e..a478e6763a41 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nincsenek regisztrált ujjlenyomatok."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Az érzékelő átmenetileg le van tiltva."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. ujj"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Ujjlenyomat használata"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"A folytatás ujjlenyomattal vagy képernyőzárral lehetséges"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Lehetővé teszi az alkalmazás számára, hogy csomagok törlését kérje."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Akkumulátoroptimalizálási beállítások mellőzésének kérése"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Az alkalmazás engedélyt kérhet az akkumulátoroptimalizálási beállítások mellőzésére."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"az összes csomag lekérdezése"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Engedélyezi az adott alkalmazás számára, hogy lássa az összes telepített csomagot."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Érintse meg kétszer a nagyítás beállításához"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nem sikerült hozzáadni a modult."</string> <string name="ime_action_go" msgid="5536744546326495436">"Ugrás"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 860dcf98cc8b..e42eccb58cb6 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Թույլ է տալիս հավելվածին պահանջել փաթեթների ջնջում:"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"հայցել մարտկոցի օպտիմալացումն անտեսելու թույլտվություն"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Հավելվածին հնարավորություն է տալիս հայցելու թույլտվություն՝ տվյալ հավելվածի համար մարտկոցի օպտիմալացումն անտեսելու համար:"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index b8649c11466c..f50c4707f02a 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -399,7 +399,7 @@ <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Memungkinkan aplikasi membuat bagian dari dirinya sendiri terus-menerus berada dalam memori. Izin ini dapat membatasi memori yang tersedia untuk aplikasi lain sehingga menjadikan ponsel lambat."</string> <string name="permlab_foregroundService" msgid="1768855976818467491">"jalankan layanan di latar depan"</string> <string name="permdesc_foregroundService" msgid="8720071450020922795">"Mengizinkan aplikasi menggunakan layanan di latar depan."</string> - <string name="permlab_getPackageSize" msgid="375391550792886641">"mengukur ruang penyimpanan apl"</string> + <string name="permlab_getPackageSize" msgid="375391550792886641">"mengukur ruang penyimpanan aplikasi"</string> <string name="permdesc_getPackageSize" msgid="742743530909966782">"Mengizinkan apl mengambil kode, data, dan ukuran temboloknya"</string> <string name="permlab_writeSettings" msgid="8057285063719277394">"ubah setelan sistem"</string> <string name="permdesc_writeSettings" msgid="8293047411196067188">"Mengizinkan apl memodifikasi data setelan sistem. Apl berbahaya dapat merusak konfigurasi sistem anda."</string> @@ -437,7 +437,7 @@ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Aplikasi ini dapat menambahkan, menghapus, atau mengubah acara kalender di ponsel. Aplikasi ini dapat mengirim pesan yang kelihatannya berasal dari pemilik kalender, atau mengubah acara tanpa memberi tahu pemilik."</string> <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"akses perintah penyedia lokasi ekstra"</string> <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Memungkinkan aplikasi mengakses perintah penyedia lokasi ekstra. Tindakan ini memungkinkan aplikasi mengganggu pengoperasian GPS atau sumber lokasi lain."</string> - <string name="permlab_accessFineLocation" msgid="6426318438195622966">"akses lokasi pasti hanya saat di latar depan"</string> + <string name="permlab_accessFineLocation" msgid="6426318438195622966">"akses lokasi akurat hanya saat di latar depan"</string> <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Aplikasi ini bisa mendapatkan lokasi pasti Anda dari layanan lokasi saat aplikasi sedang digunakan. Layanan lokasi untuk perangkat harus diaktifkan agar aplikasi bisa mendapatkan lokasi. Ini dapat meningkatkan penggunaan baterai."</string> <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"akses perkiraan lokasi hanya saat berada di latar depan"</string> <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Aplikasi ini bisa mendapatkan perkiraan lokasi Anda dari layanan lokasi saat aplikasi sedang digunakan. Layanan lokasi untuk perangkat harus diaktifkan agar aplikasi bisa mendapatkan lokasi."</string> @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tidak ada sidik jari yang terdaftar."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor dinonaktifkan untuk sementara."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan sidik jari"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gunakan sidik jari atau kunci layar"</string> @@ -658,7 +660,7 @@ <string-array name="face_error_vendor"> </string-array> <string name="face_icon_content_description" msgid="465030547475916280">"Ikon wajah"</string> - <string name="permlab_readSyncSettings" msgid="6250532864893156277">"baca setelan sinkron"</string> + <string name="permlab_readSyncSettings" msgid="6250532864893156277">"baca setelan sinkronisasi"</string> <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Memungkinkan aplikasi membaca setelan sinkronisasi untuk sebuah akun. Misalnya, izin ini dapat menentukan apakah aplikasi Orang disinkronkan dengan sebuah akun."</string> <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"nyalakan dan matikan sinkronisasi"</string> <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Memungkinkan aplikasi mengubah setelan sinkronisasi untuk sebuah akun. Misalnya, izin ini dapat digunakan untuk mengaktifkan sinkronisasi dari aplikasi Orang dengan sebuah akun."</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Teks disalin ke papan klip."</string> <string name="copied" msgid="4675902854553014676">"Disalin"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ditempelkan dari <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> menempel dari papan klip"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan konten dari papan klip Anda"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan teks yang Anda salin"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan gambar yang Anda salin"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan konten yang Anda salin"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Mengizinkan aplikasi meminta penghapusan paket."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"meminta mengabaikan pengoptimalan baterai"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Mengizinkan aplikasi meminta izin untuk mengabaikan pengoptimalan baterai bagi aplikasi tersebut."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"mengkueri semua paket"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Mengizinkan aplikasi melihat semua paket yang diinstal."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ketuk dua kali untuk kontrol perbesar/perkecil"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Tidak dapat menambahkan widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Buka"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 0cf1effb535f..bfc747668692 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Engin fingraför hafa verið skráð."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Slökkt tímabundið á skynjara."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Fingur <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Nota fingrafar"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Nota fingrafar eða skjálás"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Leyfir forriti að biðja um eyðingu pakka."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"biðja um að hunsa rafhlöðusparnað"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gerir forriti kleift að biðja um heimild til að hunsa rafhlöðusparnað fyrir forritið."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"spyrja fyrir alla pakka"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Leyfir forriti að sjá alla uppsetta pakka."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ýttu tvisvar til að opna aðdráttarstýringar"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Ekki tókst að bæta græju við."</string> <string name="ime_action_go" msgid="5536744546326495436">"Áfram"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 29dbd76514bc..708841dafac8 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nessuna impronta digitale registrata."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensore temporaneamente disattivato."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usa l\'impronta"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usa l\'impronta o il blocco schermo"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Testo copiato negli appunti."</string> <string name="copied" msgid="4675902854553014676">"Copia eseguita"</string> <string name="pasted_from_app" msgid="5627698450808256545">"Dati dell\'app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> incollati dall\'app <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"L\'app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato dati dagli appunti"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato dati dagli appunti"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato il testo che hai copiato"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato un\'immagine che hai copiato"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato i contenuti che hai copiato"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Consente a un\'applicazione di richiedere l\'eliminazione di pacchetti."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"richiesta di ignorare le ottimizzazioni della batteria"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Consente a un\'app di chiedere l\'autorizzazione a ignorare le ottimizzazioni della batteria per quell\'app."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Invio di query per tutti i pacchetti"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Consente a un\'app di visualizzare tutti i pacchetti installati."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tocca due volte per il comando dello zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Aggiunta del widget non riuscita."</string> <string name="ime_action_go" msgid="5536744546326495436">"Vai"</string> @@ -1870,8 +1870,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Aggiornato dall\'amministratore"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string> - <string name="battery_saver_description" msgid="8518809702138617167">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string> + <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Il risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string> + <string name="battery_saver_description" msgid="8518809702138617167">"Il risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string> <string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzione Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Esempio: le immagini non vengono visualizzate finché non le tocchi."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Attivare Risparmio dati?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Attiva"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index dc43bde6c1c7..fabffbf34abf 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -607,6 +607,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1025,7 +1027,7 @@ <string name="text_copied" msgid="2531420577879738860">"הטקסט הועתק ללוח."</string> <string name="copied" msgid="4675902854553014676">"ההעתקה בוצעה"</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_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> @@ -1498,10 +1500,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"מאפשרת לאפליקציה לבקש מחיקה של חבילות."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"בקשה להתעלם מאופטימיזציות של הסוללה"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"מאפשרת לאפליקציה לבקש רשות להתעלם מאופטימיזציות של הסוללה לאפליקציה הזו."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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">"לא ניתן להוסיף widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"התחלה"</string> @@ -2123,7 +2123,7 @@ <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_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> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index b282b44197bf..e275df037597 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"パッケージの削除をリクエストすることをアプリに許可します。"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"電池の最適化を無視するかどうかの確認"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"電池の最適化の無視についてアプリが確認することを許可します。"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index dbc92da44549..bd85e614ce07 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"ტექსტი დაკოპირებულია გაცვლის ბუფერში."</string> <string name="copied" msgid="4675902854553014676">"დაკოპირდა"</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_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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"აპლიკაციას შეეძლება პაკეტების წაშლის მოთხოვნა."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ბატარეის ოპტიმიზაციის იგნორირების მოთხოვნა"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"საშუალებას მისცემს აპს, მოითხოვოს მასთან დაკავშირებული ბატარეის ოპტიმიზაციის იგნორირების ნებართვა."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 6d3429e8b770..16da28fc28d9 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Қолданбаның пакеттерді жоюға рұқсат сұрауына мүмкіндік береді."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"батареяны оңтайландыру әрекетін елемеуді сұрау"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Қолданба батареяны оңтайландыру әрекетін елемеуді сұрай алады."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 19aa889245d3..e72db139ae61 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"បានចម្លងអត្ថបទទៅក្ដារតម្បៀតខ្ទាស់។"</string> <string name="copied" msgid="4675902854553014676">"បានចម្លង"</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_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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"អនុញ្ញាតឲ្យកម្មវិធីស្នើសុំលុបកញ្ចប់។"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ស្នើឲ្យមិនអើពើចំពោះការបង្កើនប្រសិទ្ធភាពថ្ម"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"អនុញ្ញាតឲ្យកម្មវិធីស្នើសុំការអនុញ្ញាត ដើម្បីមិនអើពើចំពោះការបង្កើនប្រសិទ្ធភាពថ្ម។"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index a3e0ca57ee0d..8858d0b8896d 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"ಪಠ್ಯವನ್ನು ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ನಕಲಿಸಲಾಗಿದೆ."</string> <string name="copied" msgid="4675902854553014676">"ನಕಲಿಸಲಾಗಿದೆ"</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_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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ಪ್ಯಾಕೇಜ್ಗಳನ್ನು ಅಳಿಸುವುದಕ್ಕಾಗಿ ವಿನಂತಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ಬ್ಯಾಟರಿ ಆಪ್ಟಿಮೈಸೇಶನ್ಗಳನ್ನು ಕಡೆಗಣಿಸಲು ಕೇಳಿ"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ಈ ಅಪ್ಲಿಕೇಶನ್ಗೆ ಬ್ಯಾಟರಿ ಆಪ್ಟಿಮೈಸೇಶನ್ಗಳನ್ನು ಕಡೆಗಣಿಸುವುದಕ್ಕೆ ಅನುಮತಿಯನ್ನು ಕೇಳಲು ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 4c1691b6ac5f..e9d0fc5a3326 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"애플리케이션이 패키지 삭제를 요청하도록 허용합니다."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"배터리 최적화를 무시하도록 요청"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"앱에서 배터리 최적화를 무시할 수 있는 권한을 요청할 수 있도록 허용합니다."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 66ec9aa5d42b..c1961db3c757 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Текст алмашуу буферине көчүрүлдү."</string> <string name="copied" msgid="4675902854553014676">"Көчүрүлдү"</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_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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Колдонмо топтомдорду жок кылууга уруксат сурай алат."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"батареянын кубатын көп керектей берсин"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Колдонмо батареянын кубатын керектегенден мурун уруксат суралсын."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index fd83eda7b736..65643c35bd8f 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນຮ້ອງຂໍການລຶບແພັກເກດ."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ຖາມເພື່ອໃຫ້ເພີກເສີຍການປັບແຕ່ງແບັດເຕີຣີ"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ອະນຸຍາດໃຫ້ແອັບຖາມສິດອະນຸຍາດເພື່ອເພີກເສີຍຕໍ່ການປັບແຕ່ງແບັດເຕີຣີສຳລັບແອັບນັ້ນ."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index ca43ce7ac97d..9849124541bd 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -607,6 +607,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neužregistruota jokių kontrolinių kodų."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Jutiklis laikinai išjungtas."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> pirštas"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Naudoti kontrolinį kodą"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Naudoti kontrolinį kodą arba ekrano užraktą"</string> @@ -1498,10 +1500,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Programai leidžiama pateikti užklausą dėl paketų ištrynimo."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"prašyti nepaisyti akumuliatoriaus optimizavimo nustatymų"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Programai leidžiama prašyti leidimo nepaisyti tai programai skirto akumuliatoriaus optimizavimo nustatymų."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Teikti visų paketų užklausą"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Programai leidžiama peržiūrėti visus įdiegtus paketus."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Bakstelėkite du kartus, kad valdytumėte mastelio keitimą"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nepavyko pridėti."</string> <string name="ime_action_go" msgid="5536744546326495436">"Pradėti"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 47094a4e2317..8407622b0614 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -604,6 +604,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nav reģistrēts neviens pirksta nospiedums."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensors ir īslaicīgi atspējots."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. pirksts"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Pirksta nospieduma izmantošana"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Pirksta nospieduma vai ekrāna bloķēšanas metodes izmantošana"</string> @@ -1478,10 +1480,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Atļauj lietojumprogrammai pieprasīt pakotņu dzēšanu."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Lūgt akumulatora optimizācijas ignorēšanu"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ļauj lietotnei lūgt atļauju ignorēt akumulatora optimizāciju šai lietotnei."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"pieprasīt atļauju skatīt visas pakotnes"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ļauj lietotnei skatīt visas instalētās pakotnes."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Pieskarieties divreiz, lai kontrolētu tālummaiņu."</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nevarēja pievienot logrīku."</string> <string name="ime_action_go" msgid="5536744546326495436">"Doties uz"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 5a9486e0dc28..42a55c20a190 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Дозволува апликацијата да бара бришење на пакетите."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"прашај дали да се игнорираат оптимизациите на батеријата"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Овозможува апликацијата да побара дозвола за игнорирање на оптимизациите на батеријата за таа апликација."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 9ca392a06ace..f4a79e317e0c 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"പാക്കേജുകളെ ഇല്ലാതാക്കാനുള്ള അഭ്യർത്ഥന നടത്താൻ ആപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ബാറ്ററി ഒപ്റ്റിമൈസേഷനുകൾ അവഗണിക്കാൻ ആവശ്യപ്പെടുക"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ആപ്പിന് വേണ്ടിയുള്ള ബാറ്ററി ഒപ്റ്റിമൈസേഷനുകളെ അവഗണിക്കാനുള്ള അനുമതി ചോദിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 4c01871de148..7d0a64345db5 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Апп-д багц устгах хүсэлт тавихыг зөвшөөрнө."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"батерейны оновчлол алгасахыг асуух"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Тухайн аппaaс батерейны оновчлол алгасах зөвшөөрөл асуухыг зөвшөөрдөг."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 1dfa5de9d25a..3933bf358d8f 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"अनुप्रयोगास पॅकेज हटवण्यासाठी विनंती करण्याची अनुमती देते."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"बॅटरी ऑप्टिमायझेशन दुर्लक्षित करण्यास सांगा"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"त्या ॲपसाठी बॅटरी ऑप्टिमायझेशन दुर्लक्षित करण्यासाठी ॲपला परवानगी मागण्याची अनुमती देते."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 2960ca2c0eed..2ad6baf06138 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tiada cap jari didaftarkan."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Penderia dilumpuhkan sementara."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan cap jari"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gunakan cap jari atau kunci skrin"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Membenarkan aplikasi meminta pemadaman pakej."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"minta kebenaran untuk mengabaikan pengoptimuman bateri"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Membenarkan apl meminta kebenaran untuk mengabaikan pengoptimuman bateri untuk apl itu."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"buat pertanyaan untuk semua pakej"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Membenarkan apl melihat semua pakej yang dipasang."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ketik dua kali untuk mendapatkan kawalan zum"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Tidak dapat menambahkan widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Pergi"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 2c0ed7ba01df..3543be0fb9e4 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"clipboardထံ စာသားအားကူးယူမည်"</string> <string name="copied" msgid="4675902854553014676">"မိတ္တူကူးပြီးပါပြီ"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> မှ <xliff:g id="PASTING_APP_NAME">%1$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_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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"အပလီကေးရှင်းတစ်ခုအား ပက်ကေ့ဂျ်များကို ဖျက်ရန် တောင်းဆိုခွင့်ပေးပါ။"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ဘက်ထရီ ပိုမိုကောင်းမွန်အောင် ပြုလုပ်ခြင်းကို လျစ်လျူရှုရန် တောင်းဆိုပါ"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ဘက်ထရီ ပိုမိုကောင်းမွန်အောင် ပြုလုပ်ခြင်းကို လျစ်လျူရှုရန်အတွက် ခွင့်ပြုချက်တောင်းရန် အက်ပ်ကို ခွင့်ပြုပါ။"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 0ceebf1bab93..9ed34e026e80 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ingen fingeravtrykk er registrert."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidig slått av."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Bruk fingeravtrykk"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Bruk fingeravtrykk eller skjermlås"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Lar apper be om sletting av pakker."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"be om å ignorere batterioptimaliseringer"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gjør det mulig for apper å be om tillatelse til å ignorere batterioptimaliseringer for disse appene."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"søk i alle pakker"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Lar en app se alle installerte pakker."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Trykk to ganger for zoomkontroll"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kunne ikke legge til modulen."</string> <string name="ime_action_go" msgid="5536744546326495436">"Utfør"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 8e7ca7137477..25f59c27e077 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"एपलाई प्याकेजहरू मेटाउने अनुरोध गर्न अनुमति दिन्छ।"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ब्याट्री सम्बन्धी अनुकूलनहरूलाई बेवास्ता गर्न सोध्नुहोस्"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"कुनै एपलाई त्यसका ब्याट्री सम्बन्धी अनुकूलनहरूलाई बेवास्ता गर्नाका लागि अनुमति माग्न दिन्छ।"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> @@ -2002,7 +2002,7 @@ <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_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> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 97009396cfaf..307c125c41ca 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukken geregistreerd."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor staat tijdelijk uit."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Vingerafdruk gebruiken"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Vingerafdruk of schermvergrendeling gebruiken"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Hiermee kan een app verwijdering van pakketten aanvragen."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"vragen om batterijoptimalisatie te negeren"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Hiermee kan een app rechten vragen om batterijoptimalisatie voor die app te negeren."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"alle pakketten opvragen"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Hiermee kan een app alle geïnstalleerde pakketten zien."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tik twee keer voor zoomregeling"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kan widget niet toevoegen."</string> <string name="ime_action_go" msgid="5536744546326495436">"Ga"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index d6b1e1ee66e8..1e6eb37d0132 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1239,7 +1241,7 @@ <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 OSରେ ତିଆରି ହୋଇଛି ଏବଂ ଆକସ୍ମିକ ଗତିବିଧି ଦେଖାଦେଇପାରେ। ଆପ୍ର ଏକ ଅପଡେଟ୍ ଭର୍ସନ୍ ଉପଲବ୍ଧ ରହିପାରେ।"</string> <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"ସର୍ବଦା ଦେଖାନ୍ତୁ"</string> - <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"କୌଣସି ଅପଡେଟ୍ ଅଛି କି ନାହିଁ ଦେଖନ୍ତୁ"</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>) ଏହାର ସ୍ୱ-ଲାଗୁ କରାଯାଇଥିବା ଷ୍ଟ୍ରିକ୍ଟ-ମୋଡ୍ ପଲିସୀ ଉଲ୍ଲଂଘନ କରିଛି।"</string> <string name="smv_process" msgid="1398801497130695446">"ଏହି {0/PROCESS<xliff:g id="PROCESS">%1$s</xliff:g> ନିଜ ଦ୍ୱାରା ଲାଗୁ କରାଯାଇଥିବା ଷ୍ଟ୍ରିକ୍ଟମୋଡ୍ ପଲିସୀକୁ ଉଲ୍ଲଂଘନ କରିଛି।"</string> <string name="android_upgrading_title" product="default" msgid="7279077384220829683">"ଫୋନ୍ ଅପଡେଟ୍ ହେଉଛି…"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ପ୍ୟାକେଜ୍ଗୁଡ଼ିକ ଡିଲିଟ୍ କରିବା ପାଇଁ ଅନୁରୋଧ କରିବାକୁ ଏକ ଆପ୍ଲିକେଶନକୁ ଅନୁମତି ଦେଇଥାଏ।"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ବ୍ୟାଟେରୀ ଅନୁକୂଳନ ଏଡ଼ାଇବା ପାଇଁ ପଚାରନ୍ତୁ"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ଆପ୍ ପାଇଁ ବ୍ୟାଟେରୀ ଅନୁକୂଳନ ଏଡ଼ାଇବାର ଅନୁମତି ମାଗିବା ନିମନ୍ତେ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 9a73f9b85245..fbccb257569e 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ਕਿਸੇ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਪੈਕੇਜਾਂ ਨੂੰ ਮਿਟਾਉਣ ਦੀ ਬੇਨਤੀ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ਬੈਟਰੀ ਸੁਯੋਗਤਾਵਾਂ ਨੂੰ ਅਣਡਿੱਠ ਕਰਨ ਲਈ ਪੁੱਛੋ"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ਕਿਸੇ ਐਪ ਨੂੰ ਉਸ ਵਾਸਤੇ ਬੈਟਰੀ ਸੁਯੋਗਤਾਵਾਂ ਨੂੰ ਅਣਡਿੱਠ ਕਰਨ ਲਈ ਇਜਾਜ਼ਤ ਵਾਸਤੇ ਪੁੱਛਣ ਲਈ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> @@ -2152,7 +2152,7 @@ <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">"ਨਿੱਜੀ"</string> - <string name="resolver_work_tab" msgid="2690019516263167035">"ਕੰਮ"</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> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 2781df76ea5b..0671787fc470 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -607,6 +607,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nie zarejestrowano odcisków palców."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Czujnik jest tymczasowo wyłączony."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Odcisk palca <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Używaj odcisku palca"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Używaj odcisku palca lub blokady ekranu"</string> @@ -1025,7 +1027,7 @@ <string name="text_copied" msgid="2531420577879738860">"Tekst został skopiowany do schowka."</string> <string name="copied" msgid="4675902854553014676">"Skopiowano"</string> <string name="pasted_from_app" msgid="5627698450808256545">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła dane z aplikacji <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> skopiowała dane ze schowka"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła dane ze schowka"</string> <string name="pasted_text" msgid="4298871641549173733">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła skopiowany tekst"</string> <string name="pasted_image" msgid="4729097394781491022">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła skopiowany obraz"</string> <string name="pasted_content" msgid="646276353060777131">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła skopiowane treści"</string> @@ -1498,10 +1500,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Zezwala aplikacji na żądanie usunięcia pakietów."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Prośba o ignorowanie optymalizacji wykorzystania baterii"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Zezwala aplikacji na proszenie o uprawnienia do ignorowania optymalizacji wykorzystania baterii w przypadku danej aplikacji."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"zapytanie o wszystkie pakiety"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Pozwala aplikacji wyświetlać wszystkie zainstalowane pakiety."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dotknij dwukrotnie, aby sterować powiększeniem"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nie można dodać widżetu."</string> <string name="ime_action_go" msgid="5536744546326495436">"OK"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index a60cb23b7324..8c6b146c408e 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar impressão digital ou bloqueio de tela"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite que um app solicite a exclusão de pacotes."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"solicitar que as otimizações de bateria sejam ignoradas"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que um app peça permissão para ignorar as otimizações de bateria para esse app."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que um app veja todos os pacotes instalados."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toque duas vezes para ter controle do zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 5f1046267317..0fa0e50de0cb 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registada."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporariamente desativado."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar a impressão digital"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilizar o bloqueio de ecrã ou a impressão digital"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Texto copiado para a área de transferência."</string> <string name="copied" msgid="4675902854553014676">"Copiado"</string> <string name="pasted_from_app" msgid="5627698450808256545">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou da app <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> colou a partir da área de transferência"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou a partir da área de transferência"</string> <string name="pasted_text" msgid="4298871641549173733">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou o texto que copiou"</string> <string name="pasted_image" msgid="4729097394781491022">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou uma imagem que copiou"</string> <string name="pasted_content" msgid="646276353060777131">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou o conteúdo que copiou"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite que uma app solicite a eliminação de pacotes."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"pedir para ignorar as otimizações da bateria"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que uma app solicite autorização para ignorar as otimizações da bateria para a mesma."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite a uma app ver todos os pacotes instalados."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tocar duas vezes para controlar o zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index a60cb23b7324..8c6b146c408e 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar impressão digital ou bloqueio de tela"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite que um app solicite a exclusão de pacotes."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"solicitar que as otimizações de bateria sejam ignoradas"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que um app peça permissão para ignorar as otimizações de bateria para esse app."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que um app veja todos os pacotes instalados."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toque duas vezes para ter controle do zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index dc351a23156a..7bbf3e1dd913 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -604,6 +604,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nu au fost înregistrate amprente."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzorul este dezactivat temporar."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosiți amprenta"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Folosiți amprenta sau blocarea ecranului"</string> @@ -1478,10 +1480,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite unei aplicații să solicite ștergerea pachetelor."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"să solicite ignorarea optimizărilor bateriei"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite unei aplicații să solicite permisiunea de a ignora optimizările bateriei pentru aplicația respectivă."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"să interogheze toate pachetele"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite unei aplicații să vadă toate pachetele instalate."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Apăsați de două ori pentru a controla mărirea/micșorarea"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nu s-a putut adăuga widgetul."</string> <string name="ime_action_go" msgid="5536744546326495436">"Accesați"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index ea9e292fd9fc..ebe46e8124e5 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -447,7 +447,7 @@ <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="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> @@ -457,7 +457,7 @@ <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Приложение может в любое время записывать аудио с помощью микрофона."</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="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> @@ -512,8 +512,8 @@ <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_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> @@ -540,9 +540,9 @@ <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="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="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> @@ -568,7 +568,7 @@ <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="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> @@ -607,6 +607,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1498,10 +1500,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Приложение сможет запрашивать разрешения на удаление пакетов."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Без ограничения расхода батареи"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Разрешает приложению игнорировать ограничение на расход заряда батареи."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 60641032538d..eea259472033 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ස්ථාපන පැකේජ මැකීමට යෙදුමකට ඉඩ දීම."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"බැටරි ප්රශස්තකරණ නොසලකා හැරීමට ඉල්ලන්න"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"යෙදුමකට එම යෙදුම සඳහා බැටරි ප්රශස්තකරණ නොසලකා හැරීමට අවසර ඉල්ලීමට ඉඩ දෙයි."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index e42cc0116ccf..234e3a86f54f 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -607,6 +607,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neregistrovali ste žiadne odtlačky prstov."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasne vypnutý."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst: <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použiť odtlačok prsta"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Použiť odtlačok prsta alebo zámku obrazovky"</string> @@ -1025,7 +1027,7 @@ <string name="text_copied" msgid="2531420577879738860">"Text bol skopírovaný do schránky."</string> <string name="copied" msgid="4675902854553014676">"Skopírované"</string> <string name="pasted_from_app" msgid="5627698450808256545">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> prilepila údaje z aplikácie <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> bola prilepená zo schránky"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> vložila obsah zo schránky"</string> <string name="pasted_text" msgid="4298871641549173733">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> prilepila text, ktorý ste skopírovali"</string> <string name="pasted_image" msgid="4729097394781491022">"Aplik. <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> prilepila obrázok, ktorý ste skopírovali"</string> <string name="pasted_content" msgid="646276353060777131">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> prilepila obsah, ktorý ste skopírovali"</string> @@ -1498,10 +1500,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Umožňuje aplikácii vyžiadať odstránenie balíkov."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"požiadať o ignorovanie optimalizácií výdrže batérie"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Umožňuje aplikácii požiadať o povolenie ignorovať optimalizácie výdrže batérie pre danú aplikáciu."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"dopytovať všetky balíky"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Povoľuje aplikácii čítať všetky nainštalované balíky."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dvojitým klepnutím môžete ovládať priblíženie"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Miniaplikáciu sa nepodarilo pridať."</string> <string name="ime_action_go" msgid="5536744546326495436">"Hľadať"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 506c066571ac..19c443444d89 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -607,6 +607,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ni registriranih prstnih odtisov."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tipalo je začasno onemogočeno."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Uporaba prstnega odtisa"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Uporaba prstnega odtisa ali odklepanja s poverilnico"</string> @@ -1498,10 +1500,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Omogoča aplikaciji, da zahteva brisanje paketov."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Dovoljenje za prezrtje optimizacij baterije"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Aplikaciji dovoljuje, da vpraša za dovoljenje, ali naj prezre optimizacije baterije."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"poizvedovanje po vseh paketih"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Aplikaciji dovoli, da vidi vse nameščene pakete."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tapnite dvakrat za nadzor povečave/pomanjšave"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Pripomočka ni bilo mogoče dodati."</string> <string name="ime_action_go" msgid="5536744546326495436">"Pojdi"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index ed6a6168a841..7934473206b5 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nuk ka asnjë gjurmë gishti të regjistruar."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensori është çaktivizuar përkohësisht."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Gishti <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Përdor gjurmën e gishtit"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Përdor gjurmën e gishtit ose kyçjen e ekranit"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Teksti u kopjua në kujtesën e fragmenteve."</string> <string name="copied" msgid="4675902854553014676">"U kopjua"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> u ngjit nga <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> ngjitur nga kujtesa jote e fragmenteve"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ngjiti përmbajtje nga kujtesa jote e fragmenteve"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ngjiti një tekst që kopjove"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ngjiti një imazh që kopjove"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ngjiti një përmbajtje që kopjove"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Lejon që një aplikacion të kërkojë fshirjen e paketave."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"kërko të shpërfillësh optimizimet e baterisë"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Lejon që një aplikacion të kërkojë leje për të shpërfillur optimizimet e baterisë për atë aplikacion."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kërko të gjitha paketat"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Lejon një aplikacion të shikojë të gjitha paketat e instaluara."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Trokit dy herë për të kontrolluar zmadhimin"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nuk mundi të shtonte miniaplikacion."</string> <string name="ime_action_go" msgid="5536744546326495436">"Shko"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 14f5673c1232..a0ae389e428b 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -365,11 +365,11 @@ <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="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="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> @@ -604,6 +604,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1022,7 +1024,7 @@ <string name="text_copied" msgid="2531420577879738860">"Текст је копиран у привремену меморију."</string> <string name="copied" msgid="4675902854553014676">"Копирано је"</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_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> @@ -1283,7 +1285,7 @@ <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="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> @@ -1395,7 +1397,7 @@ <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="console_running_notification_message" msgid="7892751888125174039">"Перформансе су смањене. Да бисте онемогући конзолу, проверите покретачки програм."</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> @@ -1478,10 +1480,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Омогућава да апликација захтева брисање пакета."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"тражење дозволе за игнорисање оптимизација батерије"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Дозвољава апликацији да тражи дозволу за игнорисање оптимизација батерије за ту апликацију."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index f5e1f81bdb05..34d5e2c08ff7 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Inga fingeravtryck har registrerats."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Använd ditt fingeravtryck"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Använd ditt fingeravtryck eller skärmlåset"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Tillåter att en app begär paketborttagning."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"får be om tillstånd att ignorera batterioptimering"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Appen får be om tillstånd att ignorera batterioptimering."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"fråga alla paket"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Tillåter att en app ser alla installerade paket."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Peka två gånger för zoomkontroll"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Det gick inte att lägga till widgeten."</string> <string name="ime_action_go" msgid="5536744546326495436">"Kör"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index c05f866b9d70..cc8d644ebc0c 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hakuna alama za vidole zilizojumuishwa."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Kitambuzi kimezimwa kwa muda."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Kidole cha <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Tumia alama ya kidole"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Tumia alama ya kidole au mbinu ya kufunga skrini"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Huruhusu programu kuomba idhini ya kufuta vifurushi."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"omba kupuuza uimarishji wa betri"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Huruhusu programu kuomba ruhusa ya kupuuza uimarishaji wa betri katika programu yako."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kutuma hoja kwa vifurushi vyote"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Huruhusu programu kuona vifurushi vyote vilivyosakinishwa."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Gusa mara mbili kwa udhibiti wa kuza"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Haikuweza kuongeza wijeti."</string> <string name="ime_action_go" msgid="5536744546326495436">"Nenda"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 9be38c9292a4..0d2815164b81 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"தொகுப்புகளை நீக்க கோர, ஆப்ஸை அனுமதிக்கும்."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"பேட்டரி மேம்படுத்தல்களைப் புறக்கணிப்பதற்கான அனுமதியைக் கோரு"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"பயன்பாட்டிற்கான பேட்டரி மேம்படுத்தல்களைப் புறக்கணிப்பதற்கான அனுமதியைக் கோர, ஆப்ஸை அனுமதிக்கும்."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index d910224af035..936b758a1db5 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ప్యాకేజీల తొలగింపును అభ్యర్థించడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"బ్యాటరీ అనుకూలీకరణలను విస్మరించడానికి అడగాలి"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ఆ యాప్ కోసం బ్యాటరీ అనుకూలీకరణలు విస్మరించేలా అనుమతి కోరడానికి యాప్ను అనుమతిస్తుంది."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index b6c6ed5c296a..9b2966f8586b 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -658,7 +660,7 @@ <string-array name="face_error_vendor"> </string-array> <string name="face_icon_content_description" msgid="465030547475916280">"ไอคอนใบหน้า"</string> - <string name="permlab_readSyncSettings" msgid="6250532864893156277">"อ่านการตั้งค่าการซิงค์แล้ว"</string> + <string name="permlab_readSyncSettings" msgid="6250532864893156277">"อ่านการตั้งค่าการซิงค์"</string> <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"อนุญาตให้แอปพลิเคชันอ่านการตั้งค่าการซิงค์ของบัญชี ตัวอย่างเช่น การอนุญาตนี้สามารถระบุได้ว่าแอปพลิเคชัน People ซิงค์กับบัญชีหรือไม่"</string> <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"สลับระหว่างเปิดและปิดการซิงค์"</string> <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"อนุญาตให้แอปพลิเคชันเปลี่ยนแปลงการตั้งค่าการซิงค์ของบัญชี ตัวอย่างเช่น สามารถใช้การอนุญาตเปิดใช้งานการซิงค์แอปพลิเคชัน People กับบัญชี"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"คัดลอกข้อความไปยังคลิปบอร์ด"</string> <string name="copied" msgid="4675902854553014676">"คัดลอกแล้ว"</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_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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"อนุญาตให้แอปพลิเคชันขอการลบแพ็กเกจ"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ขอเพิกเฉยต่อการเพิ่มประสิทธิภาพแบตเตอรี่"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"อนุญาตให้แอปขอสิทธิ์เพิกเฉยต่อการเพิ่มประสิทธิภาพแบตเตอรี่สำหรับแอปนั้น"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 4ea1da96a07b..a34ac289dab9 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Walang naka-enroll na fingerprint."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Pansamantalang na-disable ang sensor."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Daliri <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gumamit ng fingerprint"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gumamit ng fingerprint o lock ng screen"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Pinapayagan ang isang application na humiling ng pag-delete ng mga package."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"hilingin na balewalain ang mga pag-optimize ng baterya"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Pinapayagang humingi ng pahintulot ang isang app na balewalain ang mga pag-optimize ng baterya para sa app na iyon."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"i-query ang lahat ng package"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Nagbibigay-daan sa isang app na makita ang lahat ng naka-install na package."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tapikin ng dalawang beses para sa pagkontrol ng zoom"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Hindi maidagdag ang widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Pumunta"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 306fc8240231..a08dcb23a6fc 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Parmak izi kaydedilmedi."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensör geçici olarak devre dışı bırakıldı."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. parmak"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Parmak izi kullan"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Parmak izi veya ekran kilidi kullan"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Uygulamaya, paketleri silme isteğinde bulunma izni verir."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"pil optimizasyonlarını göz ardı etme izni iste"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Bir uygulamanın, kendisi için pil optimizasyonlarını göz ardı etme izni istemesine olanak sağlar."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"tüm paketleri sorgulama"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Uygulamaya tüm yüklü paketleri görme izni verir."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Zum denetimi için iki kez dokun"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget eklenemedi."</string> <string name="ime_action_go" msgid="5536744546326495436">"Git"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index baf4a44a3997..8be7da682be7 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -607,6 +607,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1498,10 +1500,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Додаток зможе надсилати запити на видалення пакетів."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"запитувати дозвіл ігнорувати оптимізацію використання заряду акумулятора"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Додаток зможе запитувати дозвіл ігнорувати оптимізацію використання заряду акумулятора."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 83180218325e..e0344e05a756 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"متن کو کلپ بورڈ پر کاپی کیا گیا۔"</string> <string name="copied" msgid="4675902854553014676">"کاپی ہو گیا"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> سے <xliff:g id="PASTING_APP_NAME">%1$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_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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ایپلیکیشن کو پیکجز حذف کرنے کیلئے درخواست کرنے کی اجازت ہے۔"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"بیٹری کی بہتریاں نظر انداز کرنے کا پوچھیں"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"اس ایپ کیلئے ایک ایپ کو بیٹری کی کارکردگی بہتر بنانے کو نظر انداز کرنے کی اجازت دیں۔"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 4c30b54b9c02..25a6a1aed415 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hech qanday barmoq izi qayd qilinmagan."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor vaqtincha faol emas."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Barmoq izi <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmoq izi ishlatish"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Barmoq izi yoki ekran qulfi"</string> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"Matn klipboardga nusxa olindi."</string> <string name="copied" msgid="4675902854553014676">"Nusxalandi"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> ilovasidan <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> joylandi"</string> - <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> vaqtinchalik xotiradan joylandi"</string> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> vaqtinchalik xotiradan joyladi"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> siz nusxa olgan matnni joyladi"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> siz nusxa olgan rasmni joyladi"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> siz nusxa olgan kontentni joyladi"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Ilovaga paketlarni o‘chirib tashlash so‘rovini yuborish imkonini beradi."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"batareya quvvatidan xohlagancha foydalanishni so‘rash"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ilovaga batareya quvvatidan xohlagancha foydalanish uchun ruxsat so‘rashga imkon beradi."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"barcha paketlarni chiqarish"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ilova oʻrnatilgan barcha paketlarni koʻrishiga ruxsat beradi"</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ko‘lamini o‘zgartirish uchun ikki marta bosing"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Vidjet qo‘shilmadi."</string> <string name="ime_action_go" msgid="5536744546326495436">"Tanlash"</string> @@ -1990,8 +1990,8 @@ <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Fayllarni ko‘rish uchun bosing"</string> <string name="pin_target" msgid="8036028973110156895">"Qadash"</string> <string name="pin_specific_target" msgid="7824671240625957415">"Mahkamlash: <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="unpin_target" msgid="3963318576590204447">"Olib tashlash"</string> - <string name="unpin_specific_target" msgid="3859828252160908146">"Olib tashlash: <xliff:g id="LABEL">%1$s</xliff:g>"</string> + <string name="unpin_target" msgid="3963318576590204447">"Yechib olish"</string> + <string name="unpin_specific_target" msgid="3859828252160908146">"Yechib olish: <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="app_info" msgid="6113278084877079851">"Ilova haqida"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Demo boshlanmoqda…"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 61aba14d354e..a5cfbaf9a3d9 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Chưa đăng ký vân tay."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Đã tạm thời tắt cảm biến."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Ngón tay <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Dùng vân tay"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Dùng vân tay hoặc phương thức khóa màn hình"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Cho phép ứng dụng yêu cầu xóa gói."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"hỏi để bỏ qua tối ưu hóa pin"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Cho phép ứng dụng hỏi quyền để bỏ qua tối ưu hóa pin cho ứng dụng đó."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"truy vấn tất cả các gói"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Cho phép một ứng dụng xem tất cả các gói đã cài đặt."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Nhấn hai lần để kiểm soát thu phóng"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Không thể thêm tiện ích."</string> <string name="ime_action_go" msgid="5536744546326495436">"Đến"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 7fefe747094d..23832eb16ab6 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"允许应用请求删除文件包。"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"请求忽略电池优化"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允许应用请求相应的权限,以便忽略针对该应用的电池优化。"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 457f0d518f73..0b59e5255baa 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1019,7 +1021,7 @@ <string name="text_copied" msgid="2531420577879738860">"文字已複製到剪貼簿。"</string> <string name="copied" msgid="4675902854553014676">"已複製"</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_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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"允許應用程式要求刪除套件。"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"要求忽略電池優化"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允許應用程式要求就該應用程式忽略電池優化。"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 82be3183f879..8f81d83a46e5 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -601,6 +601,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"允許應用程式要求刪除套件。"</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"要求忽略電池效能最佳化設定"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允許應用程式要求權限,以便忽略針對該應用程式的電池效能最佳化設定。"</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <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> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 85cf88cfb624..c5c781840ce5 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -601,6 +601,8 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Azikho izigxivizo zeminwe ezibhalisiwe."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Inzwa ikhutshazwe okwesikhashana."</string> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <string name="fingerprint_name_template" msgid="8941662088160289778">"Umunwe ongu-<xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sebenzisa izigxivizo zeminwe"</string> <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Sebenzisa izigxivizo zeminwe noma ukukhiya isikrini"</string> @@ -1458,10 +1460,8 @@ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Ivumela uhlelo lokusebenza ukuthi lucele ukususwa kwamaphakheji."</string> <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"cela ukuziba ukulungiselelwa kwebhethri"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ivumela uhlelo lokusebenza ukuthi licele imvume yokuziba ukulungiselela ibhethri yalolo hlelo lokusebenza."</string> - <!-- no translation found for permlab_queryAllPackages (2928450604653281650) --> - <skip /> - <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) --> - <skip /> + <string name="permlab_queryAllPackages" msgid="2928450604653281650">"buza wonke amaphakheji"</string> + <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ivumela i-app ibone wonke amaphakheji afakiwe."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Thepha kabili ukuthola ukulawula ukusondeza"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Yehlulekile ukwengeza i-widget."</string> <string name="ime_action_go" msgid="5536744546326495436">"Iya"</string> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index d94bcfb04926..55ed83b32262 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -140,7 +140,7 @@ <color name="notification_secondary_text_color_light">@color/primary_text_default_material_light</color> <item name="notification_secondary_text_disabled_alpha" format="float" type="dimen">0.38</item> <color name="notification_secondary_text_color_dark">@color/primary_text_default_material_dark</color> - <color name="notification_default_color_dark">@color/primary_text_default_material_light</color> + <color name="notification_default_color_dark">#ddffffff</color> <color name="notification_default_color_light">#a3202124</color> <color name="notification_primary_text_color_current">@color/notification_primary_text_color_light</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ee33d48768c5..3c47366a59db 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3529,6 +3529,12 @@ <!-- If true, all guest users created on the device will be ephemeral. --> <bool name="config_guestUserEphemeral">false</bool> + <!-- Whether device should always have a guest user available. If true, guest user will be + created on boot, and a new guest user will be created in the background anytime the current + guest user is removed. Instead of showing "Add guest" and "Remove guest", the UI will show + "Guest" and "Reset guest". --> + <bool name="config_guestUserAutoCreated">false</bool> + <!-- Enforce strong auth on boot. Setting this to false represents a security risk and should not be ordinarily done. The only case in which this might be permissible is in a car head unit where there are hardware mechanisms to protect the device (physical keys) and not @@ -4521,6 +4527,9 @@ --> </integer-array> + <!-- How long it takes for the HW to start illuminating after the illumination is requested. --> + <integer name="config_udfps_illumination_transition_ms">50</integer> + <!-- Indicates whether device has a power button fingerprint sensor. --> <bool name="config_is_powerbutton_fps" translatable="false" >false</bool> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index bdeff89352ea..d4ddab1ec502 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1636,6 +1636,8 @@ <string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor.</string> <!-- Generic error message shown when fingerprint is not available due to a security vulnerability. [CHAR LIMIT=50] --> <string name="fingerprint_error_security_update_required">Sensor temporarily disabled.</string> + <!-- Generic error message shown when fingerprint needs calibration [CHAR LIMIT=50] --> + <string name="fingerprint_error_bad_calibration">Sensor needs calibration</string> <!-- Template to be used to name enrolled fingerprints by default. --> <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7d685a202538..960bd639960e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -402,6 +402,7 @@ <java-symbol type="bool" name="config_supportsSystemDecorsOnSecondaryDisplays" /> <java-symbol type="bool" name="config_supportsInsecureLockScreen" /> <java-symbol type="bool" name="config_guestUserEphemeral" /> + <java-symbol type="bool" name="config_guestUserAutoCreated" /> <java-symbol type="bool" name="config_localDisplaysMirrorContent" /> <java-symbol type="array" name="config_localPrivateDisplayPorts" /> <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" /> @@ -2536,6 +2537,7 @@ <java-symbol type="string" name="fingerprint_error_no_fingerprints" /> <java-symbol type="string" name="fingerprint_error_hw_not_present" /> <java-symbol type="string" name="fingerprint_error_security_update_required" /> + <java-symbol type="string" name="fingerprint_error_bad_calibration" /> <!-- Fingerprint config --> <java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/> @@ -2590,6 +2592,7 @@ <java-symbol type="array" name="config_biometric_sensors" /> <java-symbol type="bool" name="allow_test_udfps" /> <java-symbol type="array" name="config_udfps_sensor_props" /> + <java-symbol type="integer" name="config_udfps_illumination_transition_ms" /> <java-symbol type="bool" name="config_is_powerbutton_fps" /> <java-symbol type="array" name="config_face_acquire_enroll_ignorelist" /> diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java new file mode 100644 index 000000000000..8c05978ad03c --- /dev/null +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static org.junit.Assert.assertEquals; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Test for verifying the behavior of {@link PropertyInvalidatedCache}. This test does + * not use any actual binder calls - it is entirely self-contained. + * <p> + * Build/Install/Run: + * atest FrameworksCoreTests:PropertyInvalidatedCacheTests + */ +@SmallTest +public class PropertyInvalidatedCacheTests { + + static final String CACHE_PROPERTY = "cache_key.cache_test_a"; + + // This class is a proxy for binder calls. It contains a counter that increments + // every time the class is queried. + private static class ServerProxy { + // The number of times this class was queried. + private int mCount = 0; + + // A single query. The key behavior is that the query count is incremented. + boolean query(int x) { + mCount++; + return value(x); + } + + // Return the expected value of an input, without incrementing the query count. + boolean value(int x) { + return x % 3 == 0; + } + + // Verify the count. + void verify(int x) { + assertEquals(x, mCount); + } + } + + @Test + public void testDisableCache1() { + + // A stand-in for the binder. The test verifies that calls are passed through to + // this class properly. + ServerProxy tester = new ServerProxy(); + + // Three caches, all using the same system property but one uses a different name. + PropertyInvalidatedCache<Integer, Boolean> cache1 = + new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { + @Override + protected Boolean recompute(Integer x) { + return tester.query(x); + } + }; + PropertyInvalidatedCache<Integer, Boolean> cache2 = + new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { + @Override + protected Boolean recompute(Integer x) { + return tester.query(x); + } + }; + PropertyInvalidatedCache<Integer, Boolean> cache3 = + new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") { + @Override + protected Boolean recompute(Integer x) { + return tester.query(x); + } + }; + + // Caches are enabled upon creation. + assertEquals(false, cache1.getDisabledState()); + assertEquals(false, cache2.getDisabledState()); + assertEquals(false, cache3.getDisabledState()); + + // Disable the cache1 instance. Only cache1 is disabled + cache1.disableInstance(); + assertEquals(true, cache1.getDisabledState()); + assertEquals(false, cache2.getDisabledState()); + assertEquals(false, cache3.getDisabledState()); + + // Disable cache1. This will disable cache1 and cache2 because they share the + // same name. cache3 has a different name and will not be disabled. + cache1.disableLocal(); + assertEquals(true, cache1.getDisabledState()); + assertEquals(true, cache2.getDisabledState()); + assertEquals(false, cache3.getDisabledState()); + + // Create a new cache1. Verify that the new instance is disabled. + cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { + @Override + protected Boolean recompute(Integer x) { + return tester.query(x); + } + }; + assertEquals(true, cache1.getDisabledState()); + } +} diff --git a/core/tests/coretests/src/android/os/PackageTagsListTest.java b/core/tests/coretests/src/android/os/PackageTagsListTest.java index 518e02e44b06..9034a5ccdd7f 100644 --- a/core/tests/coretests/src/android/os/PackageTagsListTest.java +++ b/core/tests/coretests/src/android/os/PackageTagsListTest.java @@ -30,6 +30,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; +import java.util.Collections; @Presubmit @RunWith(AndroidJUnit4.class) @@ -40,7 +41,8 @@ public class PackageTagsListTest { PackageTagsList.Builder builder = new PackageTagsList.Builder() .add("package1", "attr1") .add("package1", "attr2") - .add("package2"); + .add("package2") + .add("package4", Arrays.asList("attr1", "attr2")); PackageTagsList list = builder.build(); assertTrue(list.contains(builder.build())); @@ -49,10 +51,13 @@ public class PackageTagsListTest { assertTrue(list.contains("package2", "attr1")); assertTrue(list.contains("package2", "attr2")); assertTrue(list.contains("package2", "attr3")); + assertTrue(list.contains("package4", "attr1")); + assertTrue(list.contains("package4", "attr2")); assertTrue(list.containsAll("package2")); assertTrue(list.includes("package1")); assertTrue(list.includes("package2")); assertFalse(list.contains("package1", "attr3")); + assertFalse(list.contains("package4", "attr3")); assertFalse(list.containsAll("package1")); assertFalse(list.includes("package3")); @@ -92,6 +97,51 @@ public class PackageTagsListTest { } @Test + public void testPackageTagsList_Remove() { + PackageTagsList.Builder builder = new PackageTagsList.Builder() + .add("package1", "attr1") + .add("package1", "attr2") + .add("package2") + .add("package4", Arrays.asList("attr1", "attr2", "attr3")) + .add("package3", "attr1") + .remove("package1", "attr1") + .remove("package1", "attr2") + .remove("package2", "attr1") + .remove("package4", Arrays.asList("attr1", "attr2")) + .remove("package3"); + PackageTagsList list = builder.build(); + + assertTrue(list.contains(builder.build())); + assertFalse(list.contains("package1", "attr1")); + assertFalse(list.contains("package1", "attr2")); + assertTrue(list.contains("package2", "attr1")); + assertTrue(list.contains("package2", "attr2")); + assertTrue(list.contains("package2", "attr3")); + assertFalse(list.contains("package3", "attr1")); + assertFalse(list.contains("package4", "attr1")); + assertFalse(list.contains("package4", "attr2")); + assertTrue(list.contains("package4", "attr3")); + assertTrue(list.containsAll("package2")); + assertFalse(list.includes("package1")); + assertTrue(list.includes("package2")); + assertFalse(list.includes("package3")); + assertTrue(list.includes("package4")); + } + + @Test + public void testPackageTagsList_EmptyCollections() { + PackageTagsList.Builder builder = new PackageTagsList.Builder() + .add("package1", Collections.emptyList()) + .add("package2") + .remove("package2", Collections.emptyList()); + PackageTagsList list = builder.build(); + + assertTrue(list.contains(builder.build())); + assertFalse(list.contains("package1", "attr1")); + assertTrue(list.contains("package2", "attr2")); + } + + @Test public void testWriteToParcel() { PackageTagsList list = new PackageTagsList.Builder() .add("package1", "attr1") diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index 46e2772b30ca..90a9572b5560 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -44,6 +44,7 @@ import org.junit.runners.Suite; BatteryStatsUidTest.class, BatteryUsageStatsProviderTest.class, BatteryUsageStatsTest.class, + BatteryUsageStatsStoreTest.class, BatteryStatsUserLifecycleTests.class, BluetoothPowerCalculatorTest.class, BstatsCpuTimesValidationTest.class, diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java index d83645d6e0a5..cbd67c8324f4 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java @@ -20,10 +20,14 @@ import static com.google.common.truth.Truth.assertThat; import android.app.ActivityManager; import android.content.Context; +import android.os.BatteryConsumer; import android.os.BatteryManager; import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.Parcel; import android.os.Process; import android.os.UidBatteryConsumer; @@ -36,6 +40,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.File; import java.util.List; @SmallTest @@ -45,7 +50,8 @@ public class BatteryUsageStatsProviderTest { private static final long MINUTE_IN_MS = 60 * 1000; @Rule - public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345); + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345) + .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0); @Test public void test_getBatteryUsageStats() { @@ -187,4 +193,84 @@ public class BatteryUsageStatsProviderTest { mStatsRule.setTime(11500, 0); assertThat(provider.shouldUpdateStats(queries, 10000)).isTrue(); } + + @Test + public void testAggregateBatteryStats() { + Context context = InstrumentationRegistry.getContext(); + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + mStatsRule.setCurrentTime(5 * MINUTE_IN_MS); + batteryStats.resetAllStatsCmdLocked(); + + BatteryUsageStatsStore batteryUsageStatsStore = new BatteryUsageStatsStore(context, + batteryStats, new File(context.getCacheDir(), "BatteryUsageStatsProviderTest"), + new TestHandler(), Integer.MAX_VALUE); + + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, + batteryStats, batteryUsageStatsStore); + + batteryStats.noteFlashlightOnLocked(APP_UID, + 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + batteryStats.noteFlashlightOffLocked(APP_UID, + 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS); + mStatsRule.setCurrentTime(25 * MINUTE_IN_MS); + batteryStats.resetAllStatsCmdLocked(); + + batteryStats.noteFlashlightOnLocked(APP_UID, + 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + batteryStats.noteFlashlightOffLocked(APP_UID, + 50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS); + mStatsRule.setCurrentTime(55 * MINUTE_IN_MS); + batteryStats.resetAllStatsCmdLocked(); + + // This section should be ignored because the timestamp is out or range + batteryStats.noteFlashlightOnLocked(APP_UID, + 60 * MINUTE_IN_MS, 60 * MINUTE_IN_MS); + batteryStats.noteFlashlightOffLocked(APP_UID, + 70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS); + mStatsRule.setCurrentTime(75 * MINUTE_IN_MS); + batteryStats.resetAllStatsCmdLocked(); + + // This section should be ignored because it represents the current stats session + batteryStats.noteFlashlightOnLocked(APP_UID, + 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS); + batteryStats.noteFlashlightOffLocked(APP_UID, + 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS); + mStatsRule.setCurrentTime(95 * MINUTE_IN_MS); + + // Include the first and the second snapshot, but not the third or current + BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder() + .aggregateSnapshots(20 * MINUTE_IN_MS, 60 * MINUTE_IN_MS) + .build(); + final BatteryUsageStats stats = provider.getBatteryUsageStats(query); + + assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS); + assertThat(stats.getStatsEndTimestamp()).isEqualTo(55 * MINUTE_IN_MS); + assertThat(stats.getAggregateBatteryConsumer( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) + .isWithin(0.0001) + .of(180.0); // 360 mA * 0.5 hours + assertThat(stats.getAggregateBatteryConsumer( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) + .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) + .isEqualTo((10 + 20) * MINUTE_IN_MS); + final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream() + .filter(uid -> uid.getUid() == APP_UID).findFirst().get(); + assertThat(uidBatteryConsumer + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) + .isWithin(0.1) + .of(180.0); + } + + private static class TestHandler extends Handler { + TestHandler() { + super(Looper.getMainLooper()); + } + + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + msg.getCallback().run(); + return true; + } + } } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java new file mode 100644 index 000000000000..141a9fa30c85 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; + +import android.content.Context; +import android.os.BatteryManager; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.TypedXmlSerializer; +import android.util.Xml; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +public class BatteryUsageStatsStoreTest { + private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 2 * 1024; + + private final MockClocks mMockClocks = new MockClocks(); + private MockBatteryStatsImpl mBatteryStats; + private BatteryUsageStatsStore mBatteryUsageStatsStore; + private BatteryUsageStatsProvider mBatteryUsageStatsProvider; + private File mStoreDirectory; + + @Before + public void setup() { + mMockClocks.currentTime = 123; + mBatteryStats = new MockBatteryStatsImpl(mMockClocks); + mBatteryStats.setNoAutoReset(true); + mBatteryStats.setPowerProfile(mock(PowerProfile.class)); + + Context context = InstrumentationRegistry.getContext(); + + mStoreDirectory = new File(context.getCacheDir(), "BatteryUsageStatsStoreTest"); + clearDirectory(mStoreDirectory); + + mBatteryUsageStatsStore = new BatteryUsageStatsStore(context, mBatteryStats, + mStoreDirectory, new TestHandler(), MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES); + + mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mBatteryStats); + } + + @Test + public void testStoreSnapshot() { + mMockClocks.currentTime = 1_600_000; + + prepareBatteryStats(); + mBatteryStats.resetAllStatsCmdLocked(); + + final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps(); + assertThat(timestamps).hasLength(1); + assertThat(timestamps[0]).isEqualTo(1_600_000); + + final BatteryUsageStats batteryUsageStats = mBatteryUsageStatsStore.loadBatteryUsageStats( + 1_600_000); + assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(123); + assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(1_600_000); + assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000); + assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(5); + assertThat(batteryUsageStats.getAggregateBatteryConsumer( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE).getConsumedPower()) + .isEqualTo(600); // (3_600_000 - 3_000_000) / 1000 + } + + @Test + public void testGarbageCollectOldSnapshots() throws Exception { + prepareBatteryStats(); + + mMockClocks.realtime = 10_000_000; + mMockClocks.uptime = 10_000_000; + mMockClocks.currentTime = 10_000_000; + + final int snapshotFileSize = getSnapshotFileSize(); + final int numberOfSnapshots = + (int) (MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES / snapshotFileSize); + for (int i = 0; i < numberOfSnapshots + 2; i++) { + mBatteryStats.resetAllStatsCmdLocked(); + + mMockClocks.realtime += 10_000_000; + mMockClocks.uptime += 10_000_000; + mMockClocks.currentTime += 10_000_000; + prepareBatteryStats(); + } + + final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps(); + Arrays.sort(timestamps); + assertThat(timestamps).hasLength(numberOfSnapshots); + // Two snapshots (10_000_000 and 20_000_000) should have been discarded + assertThat(timestamps[0]).isEqualTo(30_000_000); + assertThat(getDirectorySize(mStoreDirectory)) + .isAtMost(MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES); + } + + @Test + public void testSavingStatsdAtomPullTimestamp() { + mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(1234); + assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp()) + .isEqualTo(1234); + mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(5478); + assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp()) + .isEqualTo(5478); + } + + private void prepareBatteryStats() { + mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100, + /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, + mMockClocks.realtime, mMockClocks.uptime, mMockClocks.currentTime); + mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100, + /* plugType */ 0, 85, 72, 3700, 3_000_000, 4_000_000, 0, + mMockClocks.realtime + 500_000, mMockClocks.uptime + 500_000, + mMockClocks.currentTime + 500_000); + } + + private void clearDirectory(File dir) { + if (dir.exists()) { + for (File child : dir.listFiles()) { + if (child.isDirectory()) { + clearDirectory(child); + } + child.delete(); + } + } + } + + private long getDirectorySize(File dir) { + long size = 0; + if (dir.exists()) { + for (File child : dir.listFiles()) { + if (child.isDirectory()) { + size += getDirectorySize(child); + } else { + size += child.length(); + } + } + } + return size; + } + + private int getSnapshotFileSize() throws IOException { + BatteryUsageStats stats = mBatteryUsageStatsProvider.getBatteryUsageStats( + new BatteryUsageStatsQuery.Builder() + .setMaxStatsAgeMs(0) + .includePowerModels() + .build()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TypedXmlSerializer serializer = Xml.newBinarySerializer(); + serializer.setOutput(out, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + stats.writeXml(serializer); + serializer.endDocument(); + return out.toByteArray().length; + } + + private static class TestHandler extends Handler { + TestHandler() { + super(Looper.getMainLooper()); + } + + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + msg.getCallback().run(); + return true; + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java index 380b4ae7e748..fedbf7a9868e 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -16,15 +16,25 @@ package com.android.internal.os; +import static android.os.BatteryConsumer.POWER_MODEL_MEASURED_ENERGY; +import static android.os.BatteryConsumer.POWER_MODEL_POWER_PROFILE; +import static android.os.BatteryConsumer.POWER_MODEL_UNDEFINED; + import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.os.BatteryConsumer; import android.os.BatteryUsageStats; import android.os.Parcel; import android.os.UidBatteryConsumer; import android.os.UserBatteryConsumer; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import android.util.Xml; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -32,50 +42,101 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; @SmallTest @RunWith(AndroidJUnit4.class) public class BatteryUsageStatsTest { + private static final int USER_ID = 42; + private static final int APP_UID1 = 271; + private static final int APP_UID2 = 314; + @Test public void testBuilder() { - BatteryUsageStats batteryUsageStats = buildBatteryUsageStats().build(); - validateBatteryUsageStats(batteryUsageStats); + BatteryUsageStats batteryUsageStats = buildBatteryUsageStats1(true).build(); + assertBatteryUsageStats1(batteryUsageStats, true); } @Test - public void testParcelability() { - final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats().build(); + public void testParcelability_smallNumberOfUids() { + final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats1(true).build(); final Parcel outParcel = Parcel.obtain(); outParcel.writeParcelable(outBatteryUsageStats, 0); final byte[] bytes = outParcel.marshall(); outParcel.recycle(); + assertThat(bytes.length).isLessThan(2000); + final Parcel inParcel = Parcel.obtain(); inParcel.unmarshall(bytes, 0, bytes.length); inParcel.setDataPosition(0); final BatteryUsageStats inBatteryUsageStats = inParcel.readParcelable(getClass().getClassLoader()); assertThat(inBatteryUsageStats).isNotNull(); - validateBatteryUsageStats(inBatteryUsageStats); + assertBatteryUsageStats1(inBatteryUsageStats, true); } + @Test + public void testParcelability_largeNumberOfUids() { + final BatteryUsageStats.Builder builder = + new BatteryUsageStats.Builder(new String[0]); + + // Without the use of a blob, this BatteryUsageStats object would generate a Parcel + // larger than 64 Kb + final int uidCount = 200; + for (int i = 0; i < uidCount; i++) { + BatteryStatsImpl.Uid mockUid = mock(BatteryStatsImpl.Uid.class); + when(mockUid.getUid()).thenReturn(i); + builder.getOrCreateUidBatteryConsumerBuilder(mockUid) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, i * 100); + } + + BatteryUsageStats outBatteryUsageStats = builder.build(); + + final Parcel parcel = Parcel.obtain(); + parcel.writeParcelable(outBatteryUsageStats, 0); + + assertThat(parcel.dataSize()).isLessThan(2000); + + // This parcel cannot be marshaled because it contains a file descriptor. + // Assuming that parcel marshaling works fine, let's just rewind the parcel. + parcel.setDataPosition(0); + + final BatteryUsageStats inBatteryUsageStats = + parcel.readParcelable(getClass().getClassLoader()); + parcel.recycle(); + + assertThat(inBatteryUsageStats.getUidBatteryConsumers()).hasSize(uidCount); + final Map<Integer, UidBatteryConsumer> consumersByUid = + inBatteryUsageStats.getUidBatteryConsumers().stream().collect( + Collectors.toMap(UidBatteryConsumer::getUid, c -> c)); + for (int i = 0; i < uidCount; i++) { + final UidBatteryConsumer uidBatteryConsumer = consumersByUid.get(i); + assertThat(uidBatteryConsumer).isNotNull(); + assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(i * 100); + } + } @Test public void testDefaultSessionDuration() { final BatteryUsageStats stats = - buildBatteryUsageStats().setStatsDuration(10000).build(); + buildBatteryUsageStats1(true).setStatsDuration(10000).build(); assertThat(stats.getStatsDuration()).isEqualTo(10000); } @Test public void testDump() { - final BatteryUsageStats stats = buildBatteryUsageStats().build(); + final BatteryUsageStats stats = buildBatteryUsageStats1(true).build(); final StringWriter out = new StringWriter(); try (PrintWriter pw = new PrintWriter(out)) { stats.dump(pw, " "); @@ -87,7 +148,7 @@ public class BatteryUsageStatsTest { assertThat(dump).contains("actual drain: 1000-2000"); assertThat(dump).contains("cpu: 20100 apps: 10100 duration: 20s 300ms"); assertThat(dump).contains("FOO: 20200 apps: 10200 duration: 20s 400ms"); - assertThat(dump).contains("UID 2000: 1200 ( screen=300 cpu=400 FOO=500 )"); + assertThat(dump).contains("UID 271: 1200 ( screen=300 cpu=400 FOO=500 )"); assertThat(dump).contains("User 42: 30.0 ( cpu=10.0 FOO=20.0 )"); } @@ -101,154 +162,297 @@ public class BatteryUsageStatsTest { assertThat(allNames).hasSize(BatteryConsumer.POWER_COMPONENT_COUNT); } - private BatteryUsageStats.Builder buildBatteryUsageStats() { + @Test + public void testAdd() { + final BatteryUsageStats stats1 = buildBatteryUsageStats1(false).build(); + final BatteryUsageStats stats2 = buildBatteryUsageStats2(new String[] {"FOO"}).build(); + + final BatteryUsageStats sum = + new BatteryUsageStats.Builder(new String[] {"FOO"}, true) + .add(stats1) + .add(stats2) + .build(); + + assertBatteryUsageStats(sum, 42345, 50, 2234, 4345, 1000, 5000, 5000); + + final List<UidBatteryConsumer> uidBatteryConsumers = + sum.getUidBatteryConsumers(); + for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { + if (uidBatteryConsumer.getUid() == APP_UID1) { + assertUidBatteryConsumer(uidBatteryConsumer, 2124, null, + 5321, 7432, 423, POWER_MODEL_POWER_PROFILE, 745, POWER_MODEL_UNDEFINED, + 956, 1167, 1478); + } else if (uidBatteryConsumer.getUid() == APP_UID2) { + assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar", + 1111, 2222, 333, POWER_MODEL_POWER_PROFILE, 444, POWER_MODEL_POWER_PROFILE, + 555, 666, 777); + } else { + fail("Unexpected UID " + uidBatteryConsumer.getUid()); + } + } + + assertAggregateBatteryConsumer(sum, + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, + 20223, 20434, 20645, 20856); + + assertAggregateBatteryConsumer(sum, + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, + 40211, 40422, 40633, 40844); + } + + @Test + public void testAdd_customComponentMismatch() { + final BatteryUsageStats.Builder builder = + new BatteryUsageStats.Builder(new String[] {"FOO"}, true); + final BatteryUsageStats stats = buildBatteryUsageStats2(new String[] {"BAR"}).build(); + + assertThrows(IllegalArgumentException.class, () -> builder.add(stats)); + } + + @Test + public void testXml() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TypedXmlSerializer serializer = Xml.newBinarySerializer(); + serializer.setOutput(out, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + final BatteryUsageStats stats = buildBatteryUsageStats1(true).build(); + stats.writeXml(serializer); + serializer.endDocument(); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + TypedXmlPullParser parser = Xml.newBinaryPullParser(); + parser.setInput(in, StandardCharsets.UTF_8.name()); + final BatteryUsageStats fromXml = BatteryUsageStats.createFromXml(parser); + + assertBatteryUsageStats1(fromXml, true); + } + + private BatteryUsageStats.Builder buildBatteryUsageStats1(boolean includeUserBatteryConsumer) { final MockClocks clocks = new MockClocks(); final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); - final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000); final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true) + new BatteryUsageStats.Builder(new String[] {"FOO"}, true) .setBatteryCapacity(4000) .setDischargePercentage(20) .setDischargedPowerRange(1000, 2000) .setStatsStartTimestamp(1000) .setStatsEndTimestamp(3000); - builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid) - .setPackageWithHighestDrain("foo") - .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000) - .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000) - .setConsumedPower( - BatteryConsumer.POWER_COMPONENT_SCREEN, 300) - .setConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, 400) - .setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500) - .setUsageDurationMillis( - BatteryConsumer.POWER_COMPONENT_CPU, 600) - .setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 800); - builder.getAggregateBatteryConsumerBuilder( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) - .setConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, 10100) - .setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200) - .setUsageDurationMillis( - BatteryConsumer.POWER_COMPONENT_CPU, 10300) - .setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400); + addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo", + 1000, 2000, + 300, POWER_MODEL_POWER_PROFILE, 400, POWER_MODEL_POWER_PROFILE, 500, 600, 800); + + addAggregateBatteryConsumer(builder, + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, 0, + 10100, 10200, 10300, 10400); + + addAggregateBatteryConsumer(builder, + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 30000, + 20100, 20200, 20300, 20400); + + + if (includeUserBatteryConsumer) { + builder.getOrCreateUserBatteryConsumerBuilder(USER_ID) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU, 10) + .setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20) + .setUsageDurationMillis( + BatteryConsumer.POWER_COMPONENT_CPU, 30) + .setUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40); + } + return builder; + } + + private BatteryUsageStats.Builder buildBatteryUsageStats2(String[] customPowerComponentNames) { + final MockClocks clocks = new MockClocks(); + final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); + + final BatteryUsageStats.Builder builder = + new BatteryUsageStats.Builder(customPowerComponentNames, true) + .setDischargePercentage(30) + .setDischargedPowerRange(1234, 2345) + .setStatsStartTimestamp(2000) + .setStatsEndTimestamp(5000); - builder.getAggregateBatteryConsumerBuilder( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) - .setConsumedPower(30000) + addUidBatteryConsumer(builder, batteryStats, APP_UID1, null, + 4321, 5432, + 123, POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_MEASURED_ENERGY, 456, 567, 678); + + addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar", + 1111, 2222, + 333, POWER_MODEL_POWER_PROFILE, 444, POWER_MODEL_POWER_PROFILE, 555, 666, 777); + + addAggregateBatteryConsumer(builder, + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, 0, + 10123, 10234, 10345, 10456); + + addAggregateBatteryConsumer(builder, + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 12345, + 20111, 20222, 20333, 20444); + + return builder; + } + + private void addUidBatteryConsumer(BatteryUsageStats.Builder builder, + MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain, + int timeInStateForeground, int timeInStateBackground, int screenPower, + int screenPowerModel, int cpuPower, int cpuPowerModel, int customComponentPower, + int cpuDuration, int customComponentDuration) { + final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(uid); + builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid) + .setPackageWithHighestDrain(packageWithHighestDrain) + .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, timeInStateForeground) + .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, timeInStateBackground) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel) .setConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, 20100) + BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel) .setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200) + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower) .setUsageDurationMillis( - BatteryConsumer.POWER_COMPONENT_CPU, 20300) + BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration) .setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20400); + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration); + } - builder.getOrCreateUserBatteryConsumerBuilder(42) + private void addAggregateBatteryConsumer(BatteryUsageStats.Builder builder, int scope, + double consumedPower, int cpuPower, int customComponentPower, int cpuDuration, + int customComponentDuration) { + builder.getAggregateBatteryConsumerBuilder(scope) + .setConsumedPower(consumedPower) .setConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, 10) + BatteryConsumer.POWER_COMPONENT_CPU, cpuPower) .setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20) + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower) .setUsageDurationMillis( - BatteryConsumer.POWER_COMPONENT_CPU, 30) + BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration) .setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40); - - return builder; + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration); } - public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) { - assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(30000); - assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000); - assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20); - assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000); - assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000); - assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(1000); - assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(3000); - assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(2000); + public void assertBatteryUsageStats1(BatteryUsageStats batteryUsageStats, + boolean includesUserBatteryConsumers) { + assertBatteryUsageStats(batteryUsageStats, 30000, 20, 1000, 2000, 1000, 3000, 2000); final List<UidBatteryConsumer> uidBatteryConsumers = batteryUsageStats.getUidBatteryConsumers(); + assertThat(uidBatteryConsumers).hasSize(1); for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { - if (uidBatteryConsumer.getUid() == 2000) { - assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo"); - assertThat(uidBatteryConsumer.getTimeInStateMs( - UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(1000); - assertThat(uidBatteryConsumer.getTimeInStateMs( - UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(2000); - assertThat(uidBatteryConsumer.getConsumedPower( - BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(300); - assertThat(uidBatteryConsumer.getConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(400); - assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(500); - assertThat(uidBatteryConsumer.getUsageDurationMillis( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(600); - assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(800); - assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1200); - assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1); - assertThat(uidBatteryConsumer.getCustomPowerComponentName( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO"); + if (uidBatteryConsumer.getUid() == APP_UID1) { + assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo", + 1000, 2000, 300, POWER_MODEL_POWER_PROFILE, 400, POWER_MODEL_POWER_PROFILE, + 500, 600, 800); } else { fail("Unexpected UID " + uidBatteryConsumer.getUid()); } } + final List<UserBatteryConsumer> userBatteryConsumers = + batteryUsageStats.getUserBatteryConsumers(); + if (includesUserBatteryConsumers) { + assertThat(userBatteryConsumers).hasSize(1); + for (UserBatteryConsumer userBatteryConsumer : userBatteryConsumers) { + if (userBatteryConsumer.getUserId() == USER_ID) { + assertUserBatteryConsumer(userBatteryConsumer, 42, 10, 20, 30, 40); + } else { + fail("Unexpected User ID " + userBatteryConsumer.getUserId()); + } + } + } else { + assertThat(userBatteryConsumers).isEmpty(); + } + + assertAggregateBatteryConsumer(batteryUsageStats, + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, + 10100, 10200, 10300, 10400); + + assertAggregateBatteryConsumer(batteryUsageStats, + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, + 20100, 20200, 20300, 20400); + } + + private void assertBatteryUsageStats(BatteryUsageStats batteryUsageStats, int consumedPower, + int dischargePercentage, int dischagePowerLower, int dischargePowerUpper, + int statsStartTimestamp, int statsEndTimestamp, int statsDuration) { + assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(consumedPower); + assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(dischargePercentage); + assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo( + dischagePowerLower); + assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo( + dischargePowerUpper); + assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(statsStartTimestamp); + assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(statsEndTimestamp); + assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(statsDuration); + } + + private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer, + int consumedPower, String packageWithHighestDrain, int timeInStateForeground, + int timeInStateBackground, int screenPower, int screenPowerModel, int cpuPower, + int cpuPowerModel, int customComponentPower, int cpuDuration, + int customComponentDuration) { + assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(consumedPower); + assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo( + packageWithHighestDrain); + assertThat(uidBatteryConsumer.getTimeInStateMs( + UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInStateForeground); + assertThat(uidBatteryConsumer.getTimeInStateMs( + UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(timeInStateBackground); + assertThat(uidBatteryConsumer.getConsumedPower( + BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower); + assertThat(uidBatteryConsumer.getPowerModel( + BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPowerModel); + assertThat(uidBatteryConsumer.getConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower); + assertThat(uidBatteryConsumer.getPowerModel( + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPowerModel); + assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower); + assertThat(uidBatteryConsumer.getUsageDurationMillis( + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration); + assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo( + customComponentDuration); + assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1); + assertThat(uidBatteryConsumer.getCustomPowerComponentName( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO"); + } + + private void assertUserBatteryConsumer(UserBatteryConsumer userBatteryConsumer, + int userId, int cpuPower, int customComponentPower, + int cpuDuration, int customComponentDuration) { + assertThat(userBatteryConsumer.getConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower); + assertThat(userBatteryConsumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower); + assertThat(userBatteryConsumer.getUsageDurationMillis( + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration); + assertThat(userBatteryConsumer.getUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo( + customComponentDuration); + assertThat(userBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1); + assertThat(userBatteryConsumer.getCustomPowerComponentName( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO"); + } + + private void assertAggregateBatteryConsumer(BatteryUsageStats batteryUsageStats, + int aggregateBatteryConsumerScopeAllApps, int cpuPower, int customComponentPower, + int cpuDuration, int customComponentDuration) { final BatteryConsumer appsBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS); + aggregateBatteryConsumerScopeAllApps); assertThat(appsBatteryConsumer.getConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100); + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower); assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200); + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower); assertThat(appsBatteryConsumer.getUsageDurationMillis( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10300); + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration); assertThat(appsBatteryConsumer.getUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10400); + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo( + customComponentDuration); assertThat(appsBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1); assertThat(appsBatteryConsumer.getCustomPowerComponentName( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO"); - - final BatteryConsumer deviceBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); - assertThat(deviceBatteryConsumer.getConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20100); - assertThat(deviceBatteryConsumer.getConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20200); - assertThat(deviceBatteryConsumer.getUsageDurationMillis( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20300); - assertThat(deviceBatteryConsumer.getUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20400); - assertThat(deviceBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1); - assertThat(deviceBatteryConsumer.getCustomPowerComponentName( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO"); - - final List<UserBatteryConsumer> userBatteryConsumers = - batteryUsageStats.getUserBatteryConsumers(); - for (UserBatteryConsumer userBatteryConsumer : userBatteryConsumers) { - if (userBatteryConsumer.getUserId() == 42) { - assertThat(userBatteryConsumer.getConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10); - assertThat(userBatteryConsumer.getConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20); - assertThat(userBatteryConsumer.getUsageDurationMillis( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(30); - assertThat(userBatteryConsumer.getUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(40); - assertThat(userBatteryConsumer.getConsumedPower()).isEqualTo(30); - assertThat(userBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1); - assertThat(userBatteryConsumer.getCustomPowerComponentName( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO"); - } else { - fail("Unexpected user ID " + userBatteryConsumer.getUserId()); - } - } } } diff --git a/data/etc/car/com.android.car.cluster.home.xml b/data/etc/car/com.android.car.cluster.home.xml index e1d2b18d7167..a3d0fcffc813 100644 --- a/data/etc/car/com.android.car.cluster.home.xml +++ b/data/etc/car/com.android.car.cluster.home.xml @@ -18,5 +18,6 @@ <privapp-permissions package="com.android.car.cluster.home"> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/> + <permission name="android.car.permission.CAR_MONITOR_INPUT"/> </privapp-permissions> </permissions> diff --git a/data/etc/car/com.android.car.rotary.xml b/data/etc/car/com.android.car.rotary.xml index eddef1acbbc7..a39b244da358 100644 --- a/data/etc/car/com.android.car.rotary.xml +++ b/data/etc/car/com.android.car.rotary.xml @@ -18,6 +18,7 @@ <privapp-permissions package="com.android.car.rotary"> <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.car.permission.CAR_MONITOR_INPUT"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 3c9086dde021..ac5e2d0fcacb 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -961,6 +961,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, + "-1003678883": { + "message": "Cleaning splash screen token=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_STARTING_WINDOW", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-1003060523": { "message": "Finish needs to pause: %s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 954d062b55e9..e141d5178570 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -753,11 +753,25 @@ public class HardwareRenderer { nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater()); } + private ASurfaceTransactionCallback mASurfaceTransactionCallback; + /** @hide */ public void setASurfaceTransactionCallback(ASurfaceTransactionCallback callback) { + // ensure callback is kept alive on the java side since weak ref is used in native code + mASurfaceTransactionCallback = callback; nSetASurfaceTransactionCallback(mNativeProxy, callback); } + private PrepareSurfaceControlForWebviewCallback mAPrepareSurfaceControlForWebviewCallback; + + /** @hide */ + public void setPrepareSurfaceControlForWebviewCallback( + PrepareSurfaceControlForWebviewCallback callback) { + // ensure callback is kept alive on the java side since weak ref is used in native code + mAPrepareSurfaceControlForWebviewCallback = callback; + nSetPrepareSurfaceControlForWebviewCallback(mNativeProxy, callback); + } + /** @hide */ public void setFrameCallback(FrameDrawingCallback callback) { nSetFrameCallback(mNativeProxy, callback); @@ -872,6 +886,19 @@ public class HardwareRenderer { session.close(); } + /** + * Interface used to receive callbacks when Webview requests a surface control. + * + * @hide + */ + public interface PrepareSurfaceControlForWebviewCallback { + /** + * Invoked when Webview calls to get a surface control. + * + */ + void prepare(); + } + /** * Interface used to receive callbacks when a transaction needs to be merged. * @@ -1370,6 +1397,9 @@ public class HardwareRenderer { private static native void nSetASurfaceTransactionCallback(long nativeProxy, ASurfaceTransactionCallback callback); + private static native void nSetPrepareSurfaceControlForWebviewCallback(long nativeProxy, + PrepareSurfaceControlForWebviewCallback callback); + private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback); private static native void nSetFrameCompleteCallback(long nativeProxy, diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java index 55fb83c81961..74fb618f8fd7 100644 --- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java +++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java @@ -60,6 +60,10 @@ public final class RippleAnimationSession { mForceSoftware = forceSoftware; } + boolean isForceSoftware() { + return mForceSoftware; + } + @NonNull RippleAnimationSession enter(Canvas canvas) { mStartTime = AnimationUtils.currentAnimationTimeMillis(); if (isHwAccelerated(canvas)) { @@ -130,7 +134,6 @@ public final class RippleAnimationSession { return this; } - private void exitHardware(RecordingCanvas canvas) { AnimationProperties<CanvasProperty<Float>, CanvasProperty<Paint>> props = getCanvasProperties(); @@ -199,6 +202,15 @@ public final class RippleAnimationSession { startAnimation(expand, loop); } + void setRadius(float radius) { + mProperties.setRadius(radius); + mProperties.getShader().setRadius(radius); + if (mCanvasProperties != null) { + mCanvasProperties.setRadius(CanvasProperty.createFloat(radius)); + mCanvasProperties.getShader().setRadius(radius); + } + } + @NonNull AnimationProperties<Float, Paint> getProperties() { return mProperties; } @@ -249,7 +261,7 @@ public final class RippleAnimationSession { static class AnimationProperties<FloatType, PaintType> { private final FloatType mProgress; - private final FloatType mMaxRadius; + private FloatType mMaxRadius; private final FloatType mNoisePhase; private final PaintType mPaint; private final RippleShader mShader; @@ -273,6 +285,10 @@ public final class RippleAnimationSession { return mProgress; } + void setRadius(FloatType radius) { + mMaxRadius = radius; + } + void setOrigin(FloatType x, FloatType y) { mX = x; mY = y; diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index fe80b5845bf5..8aba87ba3c8f 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -221,6 +221,7 @@ public class RippleDrawable extends LayerDrawable { private boolean mForceSoftware; // Patterned + private boolean mAddRipple = false; private float mTargetBackgroundOpacity; private ValueAnimator mBackgroundAnimation; private float mBackgroundOpacity; @@ -716,6 +717,7 @@ public class RippleDrawable extends LayerDrawable { } cancelExitingRipples(); + exitPatternedAnimation(); } @Override @@ -732,7 +734,7 @@ public class RippleDrawable extends LayerDrawable { } /** - * Notifies all the animating ripples that the hotspot bounds have changed. + * Notifies all the animating ripples that the hotspot bounds have changed and modify sessions. */ private void onHotspotBoundsChanged() { final int count = mExitingRipplesCount; @@ -748,6 +750,20 @@ public class RippleDrawable extends LayerDrawable { if (mBackground != null) { mBackground.onHotspotBoundsChanged(); } + float newRadius = Math.round(computeRadius()); + for (int i = 0; i < mRunningAnimations.size(); i++) { + RippleAnimationSession s = mRunningAnimations.get(i); + s.setRadius(newRadius); + s.getProperties().getShader() + .setResolution(mHotspotBounds.width(), mHotspotBounds.height()); + float cx = mHotspotBounds.centerX(), cy = mHotspotBounds.centerY(); + s.getProperties().getShader().setOrigin(cx, cy); + s.getProperties().setOrigin(cx, cy); + if (!s.isForceSoftware()) { + s.getCanvasProperties() + .setOrigin(CanvasProperty.createFloat(cx), CanvasProperty.createFloat(cy)); + } + } } /** @@ -807,7 +823,7 @@ public class RippleDrawable extends LayerDrawable { } private void startPatternedAnimation() { - mRippleActive = true; + mAddRipple = true; invalidateSelf(false); } @@ -838,41 +854,36 @@ public class RippleDrawable extends LayerDrawable { } private void drawPatterned(@NonNull Canvas canvas) { - final Rect bounds = getDirtyBounds(); + final Rect bounds = mHotspotBounds; final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); boolean useCanvasProps = shouldUseCanvasProps(canvas); - boolean changedHotspotBounds = !bounds.equals(mHotspotBounds); if (isBounded()) { - canvas.clipRect(bounds); + canvas.clipRect(getDirtyBounds()); } final float x, y, cx, cy, w, h; - if (changedHotspotBounds) { - x = mHotspotBounds.exactCenterX(); - y = mHotspotBounds.exactCenterY(); - cx = x; - cy = y; - h = mHotspotBounds.height(); - w = mHotspotBounds.width(); - useCanvasProps = false; - } else { - x = mPendingX; - y = mPendingY; - cx = bounds.centerX(); - cy = bounds.centerY(); - h = bounds.height(); - w = bounds.width(); - } - boolean shouldAnimate = mRippleActive; + boolean addRipple = mAddRipple; + cx = bounds.centerX(); + cy = bounds.centerY(); boolean shouldExit = mExitingAnimation; - mRippleActive = false; mExitingAnimation = false; - if (mRunningAnimations.size() > 0 && !shouldAnimate) { + mAddRipple = false; + if (mRunningAnimations.size() > 0 && !addRipple) { // update paint when view is invalidated getRipplePaint(); } drawContent(canvas); drawPatternedBackground(canvas, cx, cy); - if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) { + if (addRipple && mRunningAnimations.size() <= MAX_RIPPLES) { + if (mHasPending) { + x = mPendingX; + y = mPendingY; + mHasPending = false; + } else { + x = bounds.exactCenterX(); + y = bounds.exactCenterY(); + } + h = bounds.height(); + w = bounds.width(); RippleAnimationSession.AnimationProperties<Float, Paint> properties = createAnimationProperties(x, y, cx, cy, w, h); mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps) @@ -896,33 +907,13 @@ public class RippleDrawable extends LayerDrawable { CanvasProperty<Paint>> p = s.getCanvasProperties(); RecordingCanvas can = (RecordingCanvas) canvas; - CanvasProperty<Float> xProp, yProp; - if (changedHotspotBounds) { - xProp = CanvasProperty.createFloat(x); - yProp = CanvasProperty.createFloat(y); - p.getShader().setTouch(x, y); - p.getShader().setOrigin(x, y); - } else { - xProp = p.getX(); - yProp = p.getY(); - } - can.drawRipple(xProp, yProp, p.getMaxRadius(), p.getPaint(), + can.drawRipple(p.getX(), p.getY(), p.getMaxRadius(), p.getPaint(), p.getProgress(), p.getNoisePhase(), p.getColor(), p.getShader()); } else { RippleAnimationSession.AnimationProperties<Float, Paint> p = s.getProperties(); - float xProp, yProp; - if (changedHotspotBounds) { - xProp = x; - yProp = y; - p.getShader().setTouch(x, y); - p.getShader().setOrigin(x, y); - } else { - xProp = p.getX(); - yProp = p.getY(); - } float radius = p.getMaxRadius(); - canvas.drawCircle(xProp, yProp, radius, p.getPaint()); + canvas.drawCircle(p.getX(), p.getY(), radius, p.getPaint()); } } canvas.restoreToCount(saveCount); @@ -1109,6 +1100,11 @@ public class RippleDrawable extends LayerDrawable { if (mState.mRippleStyle == STYLE_SOLID) { mMaskCanvas.translate(left, top); } + if (mState.mRippleStyle == STYLE_PATTERNED) { + for (int i = 0; i < mRunningAnimations.size(); i++) { + mRunningAnimations.get(i).getProperties().getShader().setShader(mMaskShader); + } + } } private int getMaskType() { diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 4c2863e4f594..8cea869aea34 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -40,7 +40,7 @@ <integer name="long_press_dock_anim_duration">250</integer> <!-- Animation duration for translating of one handed when trigger / dismiss. --> - <integer name="config_one_handed_translate_animation_duration">800</integer> + <integer name="config_one_handed_translate_animation_duration">600</integer> <!-- One handed mode default offset % of display size --> <fraction name="config_one_handed_offset">40%</fraction> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index f7fb63d9ab98..4b1955e56a6c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -325,6 +325,13 @@ public class ShellTaskOrganizer extends TaskOrganizer { } @Override + public void onAppSplashScreenViewRemoved(int taskId) { + if (mStartingWindow != null) { + mStartingWindow.onAppSplashScreenViewRemoved(taskId); + } + } + + @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { synchronized (mLock) { onTaskAppeared(new TaskAppearedInfo(taskInfo, leash)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index afb40d1ff95c..9d65d28b21b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -284,6 +284,15 @@ public class Bubble implements BubbleViewProvider { return mTitle; } + /** + * @return the ShortcutInfo id if it exists, or the metadata shortcut id otherwise. + */ + String getShortcutId() { + return getShortcutInfo() != null + ? getShortcutInfo().getId() + : getMetadataShortcutId(); + } + String getMetadataShortcutId() { return mMetadataShortcutId; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index dfd878f63283..09fcb86e56de 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -93,6 +93,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; @@ -265,15 +266,7 @@ public class BubbleController { public void initialize() { mBubbleData.setListener(mBubbleDataListener); - mBubbleData.setSuppressionChangedListener(bubble -> { - // Make sure NoMan knows suppression state so that anyone querying it can tell. - try { - mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(), - !bubble.showInShade(), bubble.isSuppressed()); - } catch (RemoteException e) { - // Bad things have happened - } - }); + mBubbleData.setSuppressionChangedListener(this::onBubbleNotificationSuppressionChanged); mBubbleData.setPendingIntentCancelledListener(bubble -> { if (bubble.getBubbleIntent() == null) { @@ -401,6 +394,11 @@ public class BubbleController { return mImpl; } + @VisibleForTesting + public BubblesImpl.CachedState getImplCachedState() { + return mImpl.mCachedState; + } + public ShellExecutor getMainExecutor() { return mMainExecutor; } @@ -500,6 +498,18 @@ public class BubbleController { updateStack(); } + @VisibleForTesting + public void onBubbleNotificationSuppressionChanged(Bubble bubble) { + // Make sure NoMan knows suppression state so that anyone querying it can tell. + try { + mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(), + !bubble.showInShade(), bubble.isSuppressed()); + } catch (RemoteException e) { + // Bad things have happened + } + mImpl.mCachedState.updateBubbleSuppressedState(bubble); + } + /** Called when the current user changes. */ @VisibleForTesting public void onUserChanged(int newUserId) { @@ -694,7 +704,7 @@ public class BubbleController { mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys, (entries) -> { mMainExecutor.execute(() -> { for (BubbleEntry e : entries) { - if (canLaunchInActivityView(mContext, e)) { + if (canLaunchInTaskView(mContext, e)) { updateBubble(e, true /* suppressFlyout */, false /* showInShade */); } } @@ -808,11 +818,6 @@ public class BubbleController { } } - private boolean isBubbleExpanded(String key) { - return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null - && mBubbleData.getSelectedBubble().getKey().equals(key); - } - /** Promote the provided bubble from the overflow view. */ public void promoteBubbleFromOverflow(Bubble bubble) { mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK); @@ -957,14 +962,14 @@ public class BubbleController { } private void onEntryAdded(BubbleEntry entry) { - if (canLaunchInActivityView(mContext, entry)) { + if (canLaunchInTaskView(mContext, entry)) { updateBubble(entry); } } private void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) { // shouldBubbleUp checks canBubble & for bubble metadata - boolean shouldBubble = shouldBubbleUp && canLaunchInActivityView(mContext, entry); + boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry); if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE); @@ -1191,6 +1196,9 @@ public class BubbleController { mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate"); updateStack(); + + // Update the cached state for queries from SysUI + mImpl.mCachedState.update(update); } }; @@ -1295,15 +1303,16 @@ public class BubbleController { } /** - * Whether an intent is properly configured to display in an {@link android.app.ActivityView}. + * Whether an intent is properly configured to display in a + * {@link com.android.wm.shell.TaskView}. * - * Keep checks in sync with NotificationManagerService#canLaunchInActivityView. Typically + * Keep checks in sync with BubbleExtractor#canLaunchInTaskView. Typically * that should filter out any invalid bubbles, but should protect SysUI side just in case. * * @param context the context to use. * @param entry the entry to bubble. */ - static boolean canLaunchInActivityView(Context context, BubbleEntry entry) { + static boolean canLaunchInTaskView(Context context, BubbleEntry entry) { PendingIntent intent = entry.getBubbleMetadata() != null ? entry.getBubbleMetadata().getIntent() : null; @@ -1364,25 +1373,124 @@ public class BubbleController { } private class BubblesImpl implements Bubbles { + // Up-to-date cached state of bubbles data for SysUI to query from the calling thread + @VisibleForTesting + public class CachedState { + private boolean mIsStackExpanded; + private String mSelectedBubbleKey; + private HashSet<String> mSuppressedBubbleKeys = new HashSet<>(); + private HashMap<String, String> mSuppressedGroupToNotifKeys = new HashMap<>(); + private HashMap<String, Bubble> mShortcutIdToBubble = new HashMap<>(); + + private ArrayList<Bubble> mTmpBubbles = new ArrayList<>(); + + /** + * Updates the cached state based on the last full BubbleData change. + */ + synchronized void update(BubbleData.Update update) { + if (update.selectionChanged) { + mSelectedBubbleKey = update.selectedBubble != null + ? update.selectedBubble.getKey() + : null; + } + if (update.expandedChanged) { + mIsStackExpanded = update.expanded; + } + if (update.suppressedSummaryChanged) { + String summaryKey = + mBubbleData.getSummaryKey(update.suppressedSummaryGroup); + if (summaryKey != null) { + mSuppressedGroupToNotifKeys.put(update.suppressedSummaryGroup, summaryKey); + } else { + mSuppressedGroupToNotifKeys.remove(update.suppressedSummaryGroup); + } + } + + mTmpBubbles.clear(); + mTmpBubbles.addAll(update.bubbles); + mTmpBubbles.addAll(update.overflowBubbles); + + mSuppressedBubbleKeys.clear(); + mShortcutIdToBubble.clear(); + for (Bubble b : mTmpBubbles) { + mShortcutIdToBubble.put(b.getShortcutId(), b); + updateBubbleSuppressedState(b); + } + } + + /** + * Updates a specific bubble suppressed state. This is used mainly because notification + * suppression changes don't go through the same BubbleData update mechanism. + */ + synchronized void updateBubbleSuppressedState(Bubble b) { + if (!b.showInShade()) { + mSuppressedBubbleKeys.add(b.getKey()); + } else { + mSuppressedBubbleKeys.remove(b.getKey()); + } + } + + public synchronized boolean isStackExpanded() { + return mIsStackExpanded; + } + + public synchronized boolean isBubbleExpanded(String key) { + return mIsStackExpanded && key.equals(mSelectedBubbleKey); + } + + public synchronized boolean isBubbleNotificationSuppressedFromShade(String key, + String groupKey) { + return mSuppressedBubbleKeys.contains(key) + || (mSuppressedGroupToNotifKeys.containsKey(groupKey) + && key.equals(mSuppressedGroupToNotifKeys.get(groupKey))); + } + + @Nullable + public synchronized Bubble getBubbleWithShortcutId(String id) { + return mShortcutIdToBubble.get(id); + } + + synchronized void dump(PrintWriter pw) { + pw.println("BubbleImpl.CachedState state:"); + + pw.println("mIsStackExpanded: " + mIsStackExpanded); + pw.println("mSelectedBubbleKey: " + mSelectedBubbleKey); + + pw.print("mSuppressedBubbleKeys: "); + pw.println(mSuppressedBubbleKeys.size()); + for (String key : mSuppressedBubbleKeys) { + pw.println(" suppressing: " + key); + } + + pw.print("mSuppressedGroupToNotifKeys: "); + pw.println(mSuppressedGroupToNotifKeys.size()); + for (String key : mSuppressedGroupToNotifKeys.keySet()) { + pw.println(" suppressing: " + key); + } + } + } + + private CachedState mCachedState = new CachedState(); + @Override public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) { - return mMainExecutor.executeBlockingForResult(() -> { - return BubbleController.this.isBubbleNotificationSuppressedFromShade(key, groupKey); - }, Boolean.class); + return mCachedState.isBubbleNotificationSuppressedFromShade(key, groupKey); } @Override public boolean isBubbleExpanded(String key) { - return mMainExecutor.executeBlockingForResult(() -> { - return BubbleController.this.isBubbleExpanded(key); - }, Boolean.class); + return mCachedState.isBubbleExpanded(key); } @Override public boolean isStackExpanded() { - return mMainExecutor.executeBlockingForResult(() -> { - return BubbleController.this.isStackExpanded(); - }, Boolean.class); + return mCachedState.isStackExpanded(); + } + + @Override + @Nullable + public Bubble getBubbleWithShortcutId(String shortcutId) { + return mCachedState.getBubbleWithShortcutId(shortcutId); } @Override @@ -1425,14 +1533,6 @@ public class BubbleController { } @Override - @Nullable - public Bubble getBubbleWithShortcutId(String shortcutId) { - return mMainExecutor.executeBlockingForResult(() -> { - return BubbleController.this.mBubbleData.getAnyBubbleWithShortcutId(shortcutId); - }, Bubble.class); - } - - @Override public void onTaskbarChanged(Bundle b) { mMainExecutor.execute(() -> { BubbleController.this.onTaskbarChanged(b); @@ -1555,6 +1655,7 @@ public class BubbleController { try { mMainExecutor.executeBlocking(() -> { BubbleController.this.dump(fd, pw, args); + mCachedState.dump(pw); }); } catch (InterruptedException e) { Slog.e(TAG, "Failed to dump BubbleController in 2s"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 6f5cfd114688..d73ce6951e6d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -73,6 +73,7 @@ public class BubbleData { boolean expandedChanged; boolean selectionChanged; boolean orderChanged; + boolean suppressedSummaryChanged; boolean expanded; @Nullable BubbleViewProvider selectedBubble; @Nullable Bubble addedBubble; @@ -81,6 +82,7 @@ public class BubbleData { @Nullable Bubble removedOverflowBubble; @Nullable Bubble suppressedBubble; @Nullable Bubble unsuppressedBubble; + @Nullable String suppressedSummaryGroup; // Pair with Bubble and @DismissReason Integer final List<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>(); @@ -103,7 +105,9 @@ public class BubbleData { || removedOverflowBubble != null || orderChanged || suppressedBubble != null - || unsuppressedBubble != null; + || unsuppressedBubble != null + || suppressedSummaryChanged + || suppressedSummaryGroup != null; } void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) { @@ -380,6 +384,9 @@ public class BubbleData { */ void addSummaryToSuppress(String groupKey, String notifKey) { mSuppressedGroupKeys.put(groupKey, notifKey); + mStateChange.suppressedSummaryChanged = true; + mStateChange.suppressedSummaryGroup = groupKey; + dispatchPendingChanges(); } /** @@ -397,6 +404,9 @@ public class BubbleData { */ void removeSuppressedSummary(String groupKey) { mSuppressedGroupKeys.remove(groupKey); + mStateChange.suppressedSummaryChanged = true; + mStateChange.suppressedSummaryGroup = groupKey; + dispatchPendingChanges(); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index f81f086e598f..9687ec6a8168 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -90,15 +90,15 @@ public class BubbleExpandedView extends LinearLayout { private boolean mNeedsNewHeight; /** - * Whether we want the TaskView's content to be visible (alpha = 1f). If - * {@link #mIsAlphaAnimating} is true, this may not reflect the TaskView's actual alpha value - * until the animation ends. + * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If + * {@link #mIsAlphaAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha + * value until the animation ends. */ private boolean mIsContentVisible = false; /** - * Whether we're animating the TaskView's alpha value. If so, we will hold off on applying alpha - * changes from {@link #setContentVisibility} until the animation ends. + * Whether we're animating the {@code TaskView}'s alpha value. If so, we will hold off on + * applying alpha changes from {@link #setContentVisibility} until the animation ends. */ private boolean mIsAlphaAnimating = false; @@ -127,8 +127,8 @@ public class BubbleExpandedView extends LinearLayout { private BubblePositioner mPositioner; /** - * Container for the ActivityView that has a solid, round-rect background that shows if the - * ActivityView hasn't loaded. + * Container for the {@code TaskView} that has a solid, round-rect background that shows if the + * {@code TaskView} hasn't loaded. */ private final FrameLayout mExpandedViewContainer = new FrameLayout(getContext()); @@ -139,7 +139,7 @@ public class BubbleExpandedView extends LinearLayout { @Override public void onInitialized() { if (DEBUG_BUBBLE_EXPANDED_VIEW) { - Log.d(TAG, "onActivityViewReady: destroyed=" + mDestroyed + Log.d(TAG, "onInitialized: destroyed=" + mDestroyed + " initialized=" + mInitialized + " bubble=" + getBubbleKey()); } @@ -159,7 +159,7 @@ public class BubbleExpandedView extends LinearLayout { // Post to keep the lifecycle normal post(() -> { if (DEBUG_BUBBLE_EXPANDED_VIEW) { - Log.d(TAG, "onActivityViewReady: calling startActivity, bubble=" + Log.d(TAG, "onInitialized: calling startActivity, bubble=" + getBubbleKey()); } try { @@ -265,7 +265,7 @@ public class BubbleExpandedView extends LinearLayout { mCurrentPointer = mTopPointer; mPointerView.setVisibility(INVISIBLE); - // Set TaskView's alpha value as zero, since there is no view content to be shown. + // Set {@code TaskView}'s alpha value as zero, since there is no view content to be shown. setContentVisibility(false); mExpandedViewContainer.setOutlineProvider(new ViewOutlineProvider() { @@ -290,6 +290,27 @@ public class BubbleExpandedView extends LinearLayout { applyThemeAttrs(); setClipToPadding(false); + setOnTouchListener((view, motionEvent) -> { + if (mTaskView == null) { + return false; + } + + final Rect avBounds = new Rect(); + mTaskView.getBoundsOnScreen(avBounds); + + // Consume and ignore events on the expanded view padding that are within the + // {@code TaskView}'s vertical bounds. These events are part of a back gesture, and so + // they should not collapse the stack (which all other touches on areas around the AV + // would do). + if (motionEvent.getRawY() >= avBounds.top + && motionEvent.getRawY() <= avBounds.bottom + && (motionEvent.getRawX() < avBounds.left + || motionEvent.getRawX() > avBounds.right)) { + return true; + } + + return false; + }); // BubbleStackView is forced LTR, but we want to respect the locale for expanded view layout // so the Manage button appears on the right. @@ -470,7 +491,7 @@ public class BubbleExpandedView extends LinearLayout { /** * Updates the obscured touchable region for the task surface. This calls onLocationChanged, * which results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is - * useful if a view has been added or removed from on top of the ActivityView, such as the + * useful if a view has been added or removed from on top of the {@code TaskView}, such as the * manage menu. */ void updateObscuredTouchableRegion() { @@ -490,8 +511,9 @@ public class BubbleExpandedView extends LinearLayout { } /** - * Whether we are currently animating the TaskView's alpha value. If this is set to true, calls - * to {@link #setContentVisibility} will not be applied until this is set to false again. + * Whether we are currently animating the {@code TaskView}'s alpha value. If this is set to + * true, calls to {@link #setContentVisibility} will not be applied until this is set to false + * again. */ void setAlphaAnimating(boolean animating) { mIsAlphaAnimating = animating; @@ -503,8 +525,8 @@ public class BubbleExpandedView extends LinearLayout { } /** - * Sets the alpha of the underlying TaskView, since changing the expanded view's alpha does not - * affect the TaskView since it uses a Surface. + * Sets the alpha of the underlying {@code TaskView}, since changing the expanded view's alpha + * does not affect the {@code TaskView} since it uses a Surface. */ void setTaskViewAlpha(float alpha) { if (mTaskView != null) { @@ -743,9 +765,9 @@ public class BubbleExpandedView extends LinearLayout { } /** - * Cleans up anything related to the task and TaskView. If this view should be reused after this - * method is called, then {@link #initialize(BubbleController, BubbleStackView, boolean)} must - * be invoked first. + * Cleans up anything related to the task and {@code TaskView}. If this view should be reused + * after this method is called, then + * {@link #initialize(BubbleController, BubbleStackView, boolean)} must be invoked first. */ public void cleanUpExpandedState() { if (DEBUG_BUBBLE_EXPANDED_VIEW) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index c71f123459ec..92e455ce4e3a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1611,7 +1611,7 @@ public class BubbleStackView extends FrameLayout if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null && !mExpandedViewTemporarilyHidden) { if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { - // Before screenshotting, have the real ActivityView show on top of other surfaces + // Before screenshotting, have the real TaskView show on top of other surfaces // so that the screenshot doesn't flicker on top of it. mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true); } @@ -2583,8 +2583,8 @@ public class BubbleStackView extends FrameLayout } /** - * Requests a snapshot from the currently expanded bubble's ActivityView and displays it in a - * SurfaceView. This allows us to load a newly expanded bubble's Activity into the ActivityView, + * Requests a snapshot from the currently expanded bubble's TaskView and displays it in a + * SurfaceView. This allows us to load a newly expanded bubble's Activity into the TaskView, * while animating the (screenshot of the) previously selected bubble's content away. * * @param onComplete Callback to run once we're done here - called with 'false' if something @@ -2628,13 +2628,13 @@ public class BubbleStackView extends FrameLayout mAnimatingOutSurfaceContainer.setTranslationX(mExpandedViewContainer.getPaddingLeft()); mAnimatingOutSurfaceContainer.setTranslationY(0); - final int[] activityViewLocation = + final int[] taskViewLocation = mExpandedBubble.getExpandedView().getTaskViewLocationOnScreen(); final int[] surfaceViewLocation = mAnimatingOutSurfaceView.getLocationOnScreen(); - // Translate the surface to overlap the real ActivityView. + // Translate the surface to overlap the real TaskView. mAnimatingOutSurfaceContainer.setTranslationY( - activityViewLocation[1] - surfaceViewLocation[1]); + taskViewLocation[1] - surfaceViewLocation[1]); // Set the width/height of the SurfaceView to match the snapshot. mAnimatingOutSurfaceView.getLayoutParams().width = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java index 8dc05de9bb8f..3253bb06c835 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java @@ -68,6 +68,11 @@ public interface OneHanded { void setLockedDisabled(boolean locked, boolean enabled); /** + * Registers callback to notify WMShell when user tap shortcut to expand notification. + */ + void registerEventCallback(OneHandedEventCallback callback); + + /** * Registers callback to be notified after {@link OneHandedDisplayAreaOrganizer} * transition start or finish */ @@ -82,4 +87,9 @@ public interface OneHanded { * Notifies when user switch complete */ void onUserSwitch(int userId); + + /** + * Notifies when keyguard visibility changed + */ + void onKeyguardVisibilityChanged(boolean showing); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index c275d50a5d56..b5c54023c492 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -43,6 +43,7 @@ import android.util.Slog; import android.view.Surface; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -72,6 +73,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY = "com.android.internal.systemui.onehanded.gestural"; private static final int OVERLAY_ENABLED_DELAY_MS = 250; + private static final int DISPLAY_AREA_READY_RETRY_MS = 10; static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; @@ -79,6 +81,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private volatile boolean mIsSwipeToNotificationEnabled; private boolean mTaskChangeToExit; private boolean mLockedDisabled; + private boolean mKeyguardShowing; private int mUserId; private float mOffSetFraction; @@ -99,6 +102,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private final Handler mMainHandler; private final OneHandedImpl mImpl = new OneHandedImpl(); + private OneHandedEventCallback mEventCallback; private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer; @@ -288,7 +292,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mTimeoutObserver = getObserver(this::onTimeoutSettingChanged); mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged); mSwipeToNotificationEnabledObserver = - getObserver(this::onSwipeToNotificationEnabledSettingChanged); + getObserver(this::onSwipeToNotificationEnabledChanged); mDisplayController.addDisplayChangingController(mRotationController); setupCallback(); @@ -354,18 +358,27 @@ public class OneHandedController implements RemoteCallable<OneHandedController> @VisibleForTesting void startOneHanded() { - if (isLockedDisabled()) { + if (isLockedDisabled() || mKeyguardShowing) { Slog.d(TAG, "Temporary lock disabled"); return; } + + if (!mDisplayAreaOrganizer.isReady()) { + // Must wait until DisplayAreaOrganizer is ready for transitioning. + mMainExecutor.executeDelayed(this::startOneHanded, DISPLAY_AREA_READY_RETRY_MS); + return; + } + if (mState.isTransitioning() || mState.isInOneHanded()) { return; } + final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation(); if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) { Slog.w(TAG, "One handed mode only support portrait mode"); return; } + mState.setState(STATE_ENTERING); final int yOffSet = Math.round( mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction); @@ -394,6 +407,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mOneHandedUiEventLogger.writeEvent(uiEvent); } + void registerEventCallback(OneHandedEventCallback callback) { + mEventCallback = callback; + } + @VisibleForTesting void registerTransitionCallback(OneHandedTransitionCallback callback) { mDisplayAreaOrganizer.registerTransitionCallback(callback); @@ -464,8 +481,31 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } @VisibleForTesting + void notifyExpandNotification() { + if (mEventCallback != null) { + mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification()); + } + } + + @VisibleForTesting + void notifyUserConfigChanged(boolean success) { + if (!success) { + return; + } + // TODO Check UX if popup Toast to notify user when auto-enabled one-handed is good option. + Toast.makeText(mContext, R.string.one_handed_tutorial_title, Toast.LENGTH_LONG).show(); + } + + @VisibleForTesting void onActivatedActionChanged() { - if (mState.isTransitioning() || !isOneHandedEnabled()) { + if (!isOneHandedEnabled()) { + final boolean success = mOneHandedSettingsUtil.setOneHandedModeEnabled( + mContext.getContentResolver(), 1 /* Enabled for shortcut */, mUserId); + notifyUserConfigChanged(success); + } + + if (isSwipeToNotificationEnabled()) { + notifyExpandNotification(); return; } @@ -494,11 +534,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController> setOneHandedEnabled(enabled); // Also checks swipe to notification settings since they all need gesture overlay. - // Enabled overlay package may affect the current animation(e.g:Settings switch), - // so we delay 250ms to enabled overlay after switch animation finish - mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay( + setEnabledGesturalOverlay( enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - mContext.getContentResolver(), mUserId)), OVERLAY_ENABLED_DELAY_MS); + mContext.getContentResolver(), mUserId), true /* DelayExecute */); } @VisibleForTesting @@ -542,7 +580,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } @VisibleForTesting - void onSwipeToNotificationEnabledSettingChanged() { + void onSwipeToNotificationEnabledChanged() { final boolean enabled = mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver(), mUserId); @@ -551,7 +589,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> // Also checks one handed mode settings since they all need gesture overlay. setEnabledGesturalOverlay( enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - mContext.getContentResolver(), mUserId)); + mContext.getContentResolver(), mUserId), true /* DelayExecute */); } private void setupTimeoutListener() { @@ -569,11 +607,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController> return mIsOneHandedEnabled; } + @VisibleForTesting + boolean isSwipeToNotificationEnabled() { + return mIsSwipeToNotificationEnabled; + } + private void updateOneHandedEnabled() { if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) { mMainExecutor.execute(() -> stopOneHanded()); } + // Reset and align shortcut one_handed_mode_activated status with current mState + notifyShortcutState(mState.getState()); + mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); if (!mIsOneHandedEnabled) { @@ -608,12 +654,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController> if (info != null && !info.isEnabled()) { // Enable the default gestural one handed overlay. - setEnabledGesturalOverlay(true); + setEnabledGesturalOverlay(true /* enabled */, false /* delayExecute */); } } @VisibleForTesting - private void setEnabledGesturalOverlay(boolean enabled) { + private void setEnabledGesturalOverlay(boolean enabled, boolean delayExecute) { + if (mState.isTransitioning() || delayExecute) { + // Enabled overlay package may affect the current animation(e.g:Settings switch), + // so we delay 250ms to enabled overlay after switch animation finish, only delay once. + mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(enabled, false), + OVERLAY_ENABLED_DELAY_MS); + return; + } try { mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT); } catch (RemoteException e) { @@ -628,6 +681,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> if (enabled == isFeatureEnabled) { return; } + mLockedDisabled = locked && !enabled; } @@ -641,6 +695,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mTutorialHandler.onConfigurationChanged(); } + private void onKeyguardVisibilityChanged(boolean showing) { + mKeyguardShowing = showing; + } + private void onUserSwitch(int newUserId) { unregisterSettingObservers(); mUserId = newUserId; @@ -761,6 +819,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } @Override + public void registerEventCallback(OneHandedEventCallback callback) { + mMainExecutor.execute(() -> { + OneHandedController.this.registerEventCallback(callback); + }); + } + + @Override public void registerTransitionCallback(OneHandedTransitionCallback callback) { mMainExecutor.execute(() -> { OneHandedController.this.registerTransitionCallback(callback); @@ -780,6 +845,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController> OneHandedController.this.onUserSwitch(userId); }); } + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + mMainExecutor.execute(() -> { + OneHandedController.this.onKeyguardVisibilityChanged(showing); + }); + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index b8da37fd0c25..03a90c6d4677 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -61,11 +61,12 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private DisplayLayout mDisplayLayout = new DisplayLayout(); - private float mLastVisualOffset = 0; private final Rect mLastVisualDisplayBounds = new Rect(); private final Rect mDefaultDisplayBounds = new Rect(); private final OneHandedSettingsUtil mOneHandedSettingsUtil; + private boolean mIsReady; + private float mLastVisualOffset = 0; private int mEnterExitAnimationDurationMs; private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap(); @@ -157,6 +158,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { final DisplayAreaAppearedInfo info = displayAreaInfos.get(i); onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash()); } + mIsReady = true; updateDisplayBounds(); return displayAreaInfos; } @@ -164,9 +166,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void unregisterOrganizer() { super.unregisterOrganizer(); + mIsReady = false; resetWindowsOffset(); } + boolean isReady() { + return mIsReady; + } + /** * Handler for display rotation changes by {@link DisplayLayout} * @@ -184,8 +191,15 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { myUserId())) { return; } + mDisplayLayout.rotateTo(context.getResources(), toRotation); updateDisplayBounds(); + + if (mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + context.getContentResolver(), myUserId())) { + // If current settings is swipe notification, skip finishOffset. + return; + } finishOffset(0, TRANSITION_DIRECTION_EXIT); } @@ -312,6 +326,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { pw.println(mDisplayAreaTokenMap); pw.print(innerPrefix + "mDefaultDisplayBounds="); pw.println(mDefaultDisplayBounds); + pw.print(innerPrefix + "mIsReady="); + pw.println(mIsReady); pw.print(innerPrefix + "mLastVisualDisplayBounds="); pw.println(mLastVisualDisplayBounds); pw.print(innerPrefix + "mLastVisualOffset="); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java new file mode 100644 index 000000000000..d07eea271eac --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.onehanded; + +/** + * Additional callback interface for OneHanded events. + */ +public interface OneHandedEventCallback { + /** + * Called to notify expand notification shade. + */ + default void notifyExpandNotification() { + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java index 90fc823fb574..da53b359a304 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java @@ -105,6 +105,17 @@ public final class OneHandedSettingsUtil { } /** + * Sets one handed enable or disable flag from Settings provider. + * + * @return true if the value was set, false on database errors + */ + public boolean setOneHandedModeEnabled(ContentResolver resolver, int enabled, int userId) { + return Settings.Secure.putIntForUser(resolver, + Settings.Secure.ONE_HANDED_MODE_ENABLED, enabled, userId); + } + + + /** * Queries taps app to exit config from Settings provider. * * @return enable or disable taps app exit. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 6451b94caaba..324a6e27a242 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -835,11 +835,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, WindowContainerTransaction wct) { // note that this can be called when swipe-to-home or fixed-rotation is happening. // Skip this entirely if that's the case. - if ((mInSwipePipToHomeTransition || mWaitForFixedRotation) && fromRotation) { + final boolean waitForFixedRotationOnEnteringPip = mWaitForFixedRotation + && (mState != State.ENTERED_PIP); + if ((mInSwipePipToHomeTransition || waitForFixedRotationOnEnteringPip) && fromRotation) { if (DEBUG) { Log.d(TAG, "Skip onMovementBoundsChanged on rotation change" + " mInSwipePipToHomeTransition=" + mInSwipePipToHomeTransition - + " mWaitForFixedRotation=" + mWaitForFixedRotation); + + " mWaitForFixedRotation=" + mWaitForFixedRotation + + " mState=" + mState); } return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index f6b5889dda28..bc8e1e72b830 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -278,6 +278,10 @@ public class PhonePipMenuController implements PipMenuController { return; } + // Sync the menu bounds before showing it in case it is out of sync. + movePipMenu(null /* pipLeash */, null /* transaction */, stackBounds); + updateMenuBounds(stackBounds); + mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, showResizeHandle); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 841edef9172f..f0bd8a2846ed 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -235,15 +235,20 @@ public class PipResizeGestureHandler { @VisibleForTesting void onInputEvent(InputEvent ev) { + if (!mEnableDragCornerResize && !mEnablePinchResize) { + // No need to handle anything if neither form of resizing is enabled. + return; + } + // Don't allow resize when PiP is stashed. if (mPipBoundsState.isStashed()) { return; } if (ev instanceof MotionEvent) { - if (mOngoingPinchToResize) { + if (mEnablePinchResize && mOngoingPinchToResize) { onPinchResize((MotionEvent) ev); - } else { + } else if (mEnableDragCornerResize) { onDragCornerResize((MotionEvent) ev); } } @@ -318,8 +323,8 @@ public class PipResizeGestureHandler { case MotionEvent.ACTION_POINTER_DOWN: if (mEnablePinchResize && ev.getPointerCount() == 2) { onPinchResize(ev); - mOngoingPinchToResize = true; - return true; + mOngoingPinchToResize = mAllowGesture; + return mAllowGesture; } break; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java index 1a365fee3b13..9986154b051d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java @@ -235,7 +235,8 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { } void onAnimationProgress(float linearProgress) { - if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) { + if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid() + || !mSplashScreenView.isAttachedToWindow()) { return; } @@ -267,15 +268,20 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { return; } final SurfaceControl.Transaction tx = mTransactionPool.acquire(); - tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); - - SyncRtSurfaceTransactionApplier.SurfaceParams - params = new SyncRtSurfaceTransactionApplier.SurfaceParams - .Builder(mFirstWindowSurface) - .withWindowCrop(null) - .withMergeTransaction(tx) - .build(); - mApplier.scheduleApply(params); + if (mSplashScreenView.isAttachedToWindow()) { + tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); + + SyncRtSurfaceTransactionApplier.SurfaceParams + params = new SyncRtSurfaceTransactionApplier.SurfaceParams + .Builder(mFirstWindowSurface) + .withWindowCrop(null) + .withMergeTransaction(tx) + .build(); + mApplier.scheduleApply(params); + } else { + tx.setWindowCrop(mFirstWindowSurface, null); + tx.apply(); + } mTransactionPool.release(tx); Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT, @@ -287,13 +293,14 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { if (DEBUG_EXIT_ANIMATION) { Slog.v(TAG, "vanish animation finished"); } - mSplashScreenView.post(() -> { + + if (mSplashScreenView.isAttachedToWindow()) { mSplashScreenView.setVisibility(GONE); if (mFinishCallback != null) { mFinishCallback.run(); mFinishCallback = null; } - }); + } if (mShiftUpAnimation != null) { mShiftUpAnimation.finish(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index 4d33cb0452dc..46db35a6e29f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -35,6 +35,7 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.hardware.display.DisplayManager; import android.os.IBinder; +import android.os.RemoteCallback; import android.os.Trace; import android.os.UserHandle; import android.util.Slog; @@ -42,6 +43,7 @@ import android.util.SparseArray; import android.view.Choreographer; import android.view.Display; import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; @@ -121,6 +123,13 @@ public class StartingSurfaceDrawer { private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>(); + /** + * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is + * rendered and that have not yet been removed by their client. + */ + private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts = + new SparseArray<>(1); + /** Obtain proper context for showing splash screen on the provided display. */ private Context getDisplayContext(Context context, int displayId) { if (displayId == DEFAULT_DISPLAY) { @@ -386,25 +395,58 @@ public class StartingSurfaceDrawer { /** * Called when the Task wants to copy the splash screen. - * @param taskId */ public void copySplashScreenView(int taskId) { final StartingWindowRecord preView = mStartingWindowRecords.get(taskId); SplashScreenViewParcelable parcelable; - if (preView != null && preView.mContentView != null - && preView.mContentView.isCopyable()) { - parcelable = new SplashScreenViewParcelable(preView.mContentView); - preView.mContentView.onCopied(); + SplashScreenView splashScreenView = preView != null ? preView.mContentView : null; + if (splashScreenView != null && splashScreenView.isCopyable()) { + parcelable = new SplashScreenViewParcelable(splashScreenView); + parcelable.setClientCallback( + new RemoteCallback((bundle) -> mSplashScreenExecutor.execute( + () -> onAppSplashScreenViewRemoved(taskId, false)))); + splashScreenView.onCopied(); + mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost()); } else { parcelable = null; } if (DEBUG_SPLASH_SCREEN) { Slog.v(TAG, "Copying splash screen window view for task: " + taskId - + " parcelable? " + parcelable); + + " parcelable: " + parcelable); } ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable); } + /** + * Called when the {@link SplashScreenView} is removed from the client Activity view's hierarchy + * or when the Activity is clean up. + * + * @param taskId The Task id on which the splash screen was attached + */ + public void onAppSplashScreenViewRemoved(int taskId) { + onAppSplashScreenViewRemoved(taskId, true /* fromServer */); + } + + /** + * @param fromServer If true, this means the removal was notified by the server. This is only + * used for debugging purposes. + * @see #onAppSplashScreenViewRemoved(int) + */ + private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) { + SurfaceControlViewHost viewHost = + mAnimatedSplashScreenSurfaceHosts.get(taskId); + if (viewHost == null) { + return; + } + mAnimatedSplashScreenSurfaceHosts.remove(taskId); + if (DEBUG_SPLASH_SCREEN) { + String reason = fromServer ? "Server cleaned up" : "App removed"; + Slog.v(TAG, reason + "the splash screen. Releasing SurfaceControlViewHost for task:" + + taskId); + } + viewHost.getView().post(viewHost::release); + } + protected boolean addWindow(int taskId, IBinder appToken, View view, WindowManager wm, WindowManager.LayoutParams params) { boolean shouldSaveView = true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index cffc789106cb..9c1dde925762 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -150,6 +150,14 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo } /** + * @see StartingSurfaceDrawer#onAppSplashScreenViewRemoved(int) + */ + public void onAppSplashScreenViewRemoved(int taskId) { + mSplashScreenExecutor.execute( + () -> mStartingSurfaceDrawer.onAppSplashScreenViewRemoved(taskId)); + } + + /** * Called when the content of a task is ready to show, starting window can be removed. */ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index 1852279ee96c..950900337918 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -74,6 +74,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @Mock + OneHandedEventCallback mMockEventCallback; + @Mock OneHandedTouchHandler mMockTouchHandler; @Mock OneHandedTutorialHandler mMockTutorialHandler; @@ -106,6 +108,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); + when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true); when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash); when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn( mDefaultEnabled); @@ -241,7 +244,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testSettingsObserverUpdateSwipeToNotification() { - mSpiedOneHandedController.onSwipeToNotificationEnabledSettingChanged(); + mSpiedOneHandedController.onSwipeToNotificationEnabledChanged(); verify(mSpiedOneHandedController).setSwipeToNotificationEnabled(anyBoolean()); } @@ -311,6 +314,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout); testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180); mSpiedTransitionState.setState(STATE_NONE); + when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true); when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); @@ -372,8 +376,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); mSpiedOneHandedController.onActivatedActionChanged(); - verify(mSpiedOneHandedController, never()).startOneHanded(); - verify(mSpiedOneHandedController, never()).stopOneHanded(); + verify(mSpiedTransitionState, never()).setState(STATE_EXITING); } @Test @@ -383,20 +386,20 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); mSpiedOneHandedController.onActivatedActionChanged(); - verify(mSpiedOneHandedController, never()).startOneHanded(); - verify(mSpiedOneHandedController, never()).stopOneHanded(); + verify(mSpiedTransitionState, never()).setState(STATE_ENTERING); } @Test - public void testOneHandedDisabled_shortcutEnabled_skipActions() { + public void testOneHandedDisabled_shortcutTrigger_thenAutoEnabled() { when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); when(mSpiedTransitionState.isTransitioning()).thenReturn(false); - when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false); + when(mMockSettingsUitl.setOneHandedModeEnabled(any(), anyInt(), anyInt())).thenReturn( + false); mSpiedOneHandedController.onActivatedActionChanged(); - verify(mSpiedOneHandedController, never()).startOneHanded(); - verify(mSpiedOneHandedController, never()).stopOneHanded(); + verify(mSpiedOneHandedController).notifyUserConfigChanged(anyBoolean()); } @Test @@ -408,4 +411,41 @@ public class OneHandedControllerTest extends OneHandedTestCase { verify(mSpiedTransitionState).addSListeners(mMockTutorialHandler); } + + @Test + public void testNotifyEventCallbackWithMainExecutor() { + when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true); + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true); + mSpiedOneHandedController.registerEventCallback(mMockEventCallback); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mMockShellMainExecutor).execute(any()); + } + + @Test + public void testNotifyShortcutState_whenUpdateOneHandedEnabled() { + when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true); + mSpiedOneHandedController.registerEventCallback(mMockEventCallback); + mSpiedOneHandedController.setOneHandedEnabled(true); + + verify(mSpiedOneHandedController).notifyShortcutState(anyInt()); + } + + @Test + public void testNotifyExpandNotification_withNullCheckProtection() { + when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true); + mSpiedOneHandedController.setOneHandedEnabled(true); + mSpiedOneHandedController.notifyExpandNotification(); + + // Verify no NPE crash and mMockShellMainExecutor never be execute. + verify(mMockShellMainExecutor, never()).execute(any()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java index a27ed114de70..ef16fd391235 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java @@ -418,4 +418,18 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(); } + + @Test + public void testDisplayArea_notReadyForTransition() { + OneHandedDisplayAreaOrganizer testSpiedDisplayAreaOrganizer = spy( + new OneHandedDisplayAreaOrganizer(mContext, + mDisplayLayout, + mMockSettingsUitl, + mMockAnimationController, + mTutorialHandler, + mMockBackgroundOrganizer, + mMockShellMainExecutor)); + + assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse(); + } } diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 332f7e6f0eac..44c335f6adb3 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -455,8 +455,7 @@ void RenderNode::destroyLayers() { if (hasLayer()) { this->setLayerSurface(nullptr); } - mSnapshotResult.snapshot = nullptr; - mTargetImageFilter = nullptr; + if (mDisplayList) { mDisplayList.updateChildren([](RenderNode* child) { child->destroyLayers(); }); } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 8595b6e5f78a..c770150650e2 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -330,6 +330,11 @@ public: } else { mSkiaLayer.reset(); } + + // Clear out the previous snapshot and the image filter the previous + // snapshot was created with whenever the layer changes. + mSnapshotResult.snapshot = nullptr; + mTargetImageFilter = nullptr; } /** diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp index d9b9e2456fff..92e20c477669 100644 --- a/libs/hwui/WebViewFunctorManager.cpp +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -197,6 +197,8 @@ ASurfaceControl* WebViewFunctor::getSurfaceControl() { auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions(); mSurfaceControl = funcs.createFunc(rootSurfaceControl, "Webview Overlay SurfaceControl"); ASurfaceTransaction* transaction = funcs.transactionCreateFunc(); + activeContext->prepareSurfaceControlForWebview(); + funcs.transactionSetZOrderFunc(transaction, mSurfaceControl, -1); funcs.transactionSetVisibilityFunc(transaction, mSurfaceControl, ASURFACE_TRANSACTION_VISIBILITY_SHOW); funcs.transactionApplyFunc(transaction); diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 602c32a966d3..9ff2f461d40d 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -71,6 +71,10 @@ struct { } gASurfaceTransactionCallback; struct { + jmethodID prepare; +} gPrepareSurfaceControlForWebviewCallback; + +struct { jmethodID onFrameDraw; } gFrameDrawingCallback; @@ -500,6 +504,28 @@ private: jobject mObject; }; +class JWeakGlobalRefHolder { +public: + JWeakGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm) { + mWeakRef = getenv(vm)->NewWeakGlobalRef(object); + } + + virtual ~JWeakGlobalRefHolder() { + if (mWeakRef != nullptr) getenv(mVm)->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + } + + jobject ref() { return mWeakRef; } + JavaVM* vm() { return mVm; } + +private: + JWeakGlobalRefHolder(const JWeakGlobalRefHolder&) = delete; + void operator=(const JWeakGlobalRefHolder&) = delete; + + JavaVM* mVm; + jobject mWeakRef; +}; + using TextureMap = std::unordered_map<uint32_t, sk_sp<SkImage>>; struct PictureCaptureState { @@ -633,19 +659,45 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback( } else { JavaVM* vm = nullptr; LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); - auto globalCallbackRef = std::make_shared<JGlobalRefHolder>( - vm, env->NewGlobalRef(aSurfaceTransactionCallback)); + auto globalCallbackRef = + std::make_shared<JWeakGlobalRefHolder>(vm, aSurfaceTransactionCallback); proxy->setASurfaceTransactionCallback( [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) { JNIEnv* env = getenv(globalCallbackRef->vm()); - env->CallVoidMethod(globalCallbackRef->object(), - gASurfaceTransactionCallback.onMergeTransaction, + jobject localref = env->NewLocalRef(globalCallbackRef->ref()); + if (CC_UNLIKELY(!localref)) { + return; + } + env->CallVoidMethod(localref, gASurfaceTransactionCallback.onMergeTransaction, static_cast<jlong>(transObj), static_cast<jlong>(scObj), static_cast<jlong>(frameNr)); + env->DeleteLocalRef(localref); }); } } +static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback( + JNIEnv* env, jobject clazz, jlong proxyPtr, jobject callback) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + if (!callback) { + proxy->setPrepareSurfaceControlForWebviewCallback(nullptr); + } else { + JavaVM* vm = nullptr; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); + auto globalCallbackRef = + std::make_shared<JWeakGlobalRefHolder>(vm, callback); + proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() { + JNIEnv* env = getenv(globalCallbackRef->vm()); + jobject localref = env->NewLocalRef(globalCallbackRef->ref()); + if (CC_UNLIKELY(!localref)) { + return; + } + env->CallVoidMethod(localref, gPrepareSurfaceControlForWebviewCallback.prepare); + env->DeleteLocalRef(localref); + }); + } +} + static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject frameCallback) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); @@ -944,6 +996,9 @@ static const JNINativeMethod gMethods[] = { {"nSetASurfaceTransactionCallback", "(JLandroid/graphics/HardwareRenderer$ASurfaceTransactionCallback;)V", (void*)android_view_ThreadedRenderer_setASurfaceTransactionCallback}, + {"nSetPrepareSurfaceControlForWebviewCallback", + "(JLandroid/graphics/HardwareRenderer$PrepareSurfaceControlForWebviewCallback;)V", + (void*)android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback}, {"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V", (void*)android_view_ThreadedRenderer_setFrameCallback}, {"nSetFrameCompleteCallback", @@ -1011,6 +1066,11 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) { gASurfaceTransactionCallback.onMergeTransaction = GetMethodIDOrDie(env, aSurfaceTransactionCallbackClass, "onMergeTransaction", "(JJJ)V"); + jclass prepareSurfaceControlForWebviewCallbackClass = FindClassOrDie( + env, "android/graphics/HardwareRenderer$PrepareSurfaceControlForWebviewCallback"); + gPrepareSurfaceControlForWebviewCallback.prepare = + GetMethodIDOrDie(env, prepareSurfaceControlForWebviewCallbackClass, "prepare", "()V"); + jclass frameCallbackClass = FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameDrawingCallback"); gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 8bfc2c14ad7c..44d0038ad47e 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -174,16 +174,12 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { ATRACE_CALL(); if (window) { - int extraBuffers = 0; - native_window_get_extra_buffer_count(window, &extraBuffers); - mNativeSurface = std::make_unique<ReliableSurface>(window); mNativeSurface->init(); if (enableTimeout) { // TODO: Fix error handling & re-shorten timeout ANativeWindow_setDequeueTimeout(window, 4000_ms); } - mNativeSurface->setExtraBufferCount(extraBuffers); } else { mNativeSurface = nullptr; } @@ -197,6 +193,7 @@ void CanvasContext::setSurfaceControl(ASurfaceControl* surfaceControl) { if (surfaceControl == nullptr) { setASurfaceTransactionCallback(nullptr); + setPrepareSurfaceControlForWebviewCallback(nullptr); } if (mSurfaceControl != nullptr) { @@ -918,6 +915,12 @@ bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction, ASurfaceC return true; } +void CanvasContext::prepareSurfaceControlForWebview() { + if (mPrepareSurfaceControlForWebviewCallback) { + std::invoke(mPrepareSurfaceControlForWebviewCallback); + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 4bdc2514db8c..a61c2bfd5c01 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -212,6 +212,12 @@ public: bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control); + void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback) { + mPrepareSurfaceControlForWebviewCallback = callback; + } + + void prepareSurfaceControlForWebview(); + static CanvasContext* getActiveContext(); private: @@ -312,6 +318,8 @@ private: bool mExpectSurfaceStats = false; std::function<void(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback; + std::function<void()> mPrepareSurfaceControlForWebviewCallback; + void cleanupResources(); }; diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index c29cc11fa7ea..6df34bee2224 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -278,7 +278,6 @@ int ReliableSurface::hook_query(const ANativeWindow *window, ANativeWindow_query int result = query(window, what, value); if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) { std::lock_guard _lock{rs->mMutex}; - *value += rs->mExtraBuffers; rs->mExpectedBufferCount = *value + 2; } return result; diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 41969e776fc8..595964741049 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -51,11 +51,6 @@ public: return ret; } - void setExtraBufferCount(size_t extraBuffers) { - std::lock_guard _lock{mMutex}; - mExtraBuffers = extraBuffers; - } - bool didSetExtraBuffers() const { std::lock_guard _lock{mMutex}; return mDidSetExtraBuffers; @@ -73,7 +68,6 @@ private: base::unique_fd mReservedFenceFd; bool mHasDequeuedBuffer = false; int mBufferQueueState = OK; - size_t mExtraBuffers = 0; size_t mExpectedBufferCount = 0; bool mDidSetExtraBuffers = false; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 6fd644bfa28e..c47050c31e9a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -319,6 +319,12 @@ void RenderProxy::setASurfaceTransactionCallback( [this, cb = callback]() { mContext->setASurfaceTransactionCallback(cb); }); } +void RenderProxy::setPrepareSurfaceControlForWebviewCallback( + const std::function<void()>& callback) { + mRenderThread.queue().post( + [this, cb = callback]() { mContext->setPrepareSurfaceControlForWebviewCallback(cb); }); +} + void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) { mDrawFrameTask.setFrameCallback(std::move(callback)); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 6d80949a4eba..d575aa77e4ab 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -124,6 +124,7 @@ public: void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback); void setASurfaceTransactionCallback( const std::function<void(int64_t, int64_t, int64_t)>& callback); + void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback); void setFrameCallback(std::function<void(int64_t)>&& callback); void setFrameCompleteCallback(std::function<void(int64_t)>&& callback); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 4ba774801bba..524407d2b9b0 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -102,6 +102,10 @@ ASurfaceControlFunctions::ASurfaceControlFunctions() { (AST_setVisibility)dlsym(handle_, "ASurfaceTransaction_setVisibility"); LOG_ALWAYS_FATAL_IF(transactionSetVisibilityFunc == nullptr, "Failed to find required symbol ASurfaceTransaction_setVisibility!"); + + transactionSetZOrderFunc = (AST_setZOrder)dlsym(handle_, "ASurfaceTransaction_setZOrder"); + LOG_ALWAYS_FATAL_IF(transactionSetZOrderFunc == nullptr, + "Failed to find required symbol ASurfaceTransaction_setZOrder!"); } void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index dd060097ddf7..c5e3746587b8 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -96,6 +96,8 @@ typedef void (*AST_delete)(ASurfaceTransaction* transaction); typedef void (*AST_apply)(ASurfaceTransaction* transaction); typedef void (*AST_setVisibility)(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int8_t visibility); +typedef void (*AST_setZOrder)(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, + int32_t z_order); struct ASurfaceControlFunctions { ASurfaceControlFunctions(); @@ -112,6 +114,7 @@ struct ASurfaceControlFunctions { AST_delete transactionDeleteFunc; AST_apply transactionApplyFunc; AST_setVisibility transactionSetVisibilityFunc; + AST_setZOrder transactionSetZOrderFunc; }; class ChoreographerSource; diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index f4466782ac8a..4cd3616566c2 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -363,9 +363,9 @@ public final class GnssMeasurement implements Parcelable { } /** - * Gets per-satellite sync state. + * Gets per-satellite-signal sync state. * - * <p>It represents the current sync state for the associated satellite. + * <p>It represents the current sync state for the associated satellite signal. * * <p>This value helps interpret {@link #getReceivedSvTimeNanos()}. */ diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java index 763835c9cbe2..d59756d02348 100644 --- a/location/java/android/location/LocationManagerInternal.java +++ b/location/java/android/location/LocationManagerInternal.java @@ -16,14 +16,10 @@ package android.location; - import android.annotation.NonNull; import android.annotation.Nullable; import android.location.util.identity.CallerIdentity; - -import com.android.internal.annotations.Immutable; - -import java.util.Set; +import android.os.PackageTagsList; /** * Location manager local system service interface. @@ -43,18 +39,14 @@ public abstract class LocationManagerInternal { } /** - * Interface for getting callbacks when a location provider's location tags change. - * - * @see LocationTagInfo + * Interface for getting callbacks when an app id's location provider package tags change. */ - public interface OnProviderLocationTagsChangeListener { + public interface LocationPackageTagsListener { /** - * Called when the location tags for a provider change. - * - * @param providerLocationTagInfo The tag info for a provider. + * Called when the package tags for a location provider change for a uid. */ - void onLocationTagsChanged(@NonNull LocationTagInfo providerLocationTagInfo); + void onLocationPackageTagsChanged(int uid, @NonNull PackageTagsList packageTagsList); } /** @@ -109,58 +101,9 @@ public abstract class LocationManagerInternal { public abstract @Nullable LocationTime getGnssTimeMillis(); /** - * Sets a listener for changes in the location providers' tags. Passing + * Sets a listener for changes in an app id's location provider package tags. Passing * {@code null} clears the current listener. - * - * @param listener The listener. */ - public abstract void setOnProviderLocationTagsChangeListener( - @Nullable OnProviderLocationTagsChangeListener listener); - - /** - * This class represents the location permission tags used by the location provider - * packages in a given UID. These tags are strictly used for accessing state guarded - * by the location permission(s) by a location provider which are required for the - * provider to fulfill its function as being a location provider. - */ - @Immutable - public static class LocationTagInfo { - private final int mUid; - - @NonNull - private final String mPackageName; - - @Nullable - private final Set<String> mLocationTags; - - public LocationTagInfo(int uid, @NonNull String packageName, - @Nullable Set<String> locationTags) { - mUid = uid; - mPackageName = packageName; - mLocationTags = locationTags; - } - - /** - * @return The UID for which tags are related. - */ - public int getUid() { - return mUid; - } - - /** - * @return The package for which tags are related. - */ - @NonNull - public String getPackageName() { - return mPackageName; - } - - /** - * @return The tags for the package used for location related accesses. - */ - @Nullable - public Set<String> getTags() { - return mLocationTags; - } - } + public abstract void setLocationPackageTagsListener( + @Nullable LocationPackageTagsListener listener); } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index 8de507404ca9..6dc05ad010e6 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -158,7 +158,8 @@ public class CameraBinderTest extends AndroidTestCase { ICamera cameraUser = mUtils.getCameraService() .connect(dummyCallbacks, cameraId, clientPackageName, ICameraService.USE_CALLING_UID, - ICameraService.USE_CALLING_PID); + ICameraService.USE_CALLING_PID, + getContext().getApplicationInfo().targetSdkVersion); assertNotNull(String.format("Camera %s was null", cameraId), cameraUser); Log.v(TAG, String.format("Camera %s connected", cameraId)); @@ -262,7 +263,8 @@ public class CameraBinderTest extends AndroidTestCase { mUtils.getCameraService().connectDevice( dummyCallbacks, String.valueOf(cameraId), clientPackageName, clientAttributionTag, - ICameraService.USE_CALLING_UID, 0 /*oomScoreOffset*/); + ICameraService.USE_CALLING_UID, 0 /*oomScoreOffset*/, + getContext().getApplicationInfo().targetSdkVersion); assertNotNull(String.format("Camera %s was null", cameraId), cameraUser); Log.v(TAG, String.format("Camera %s connected", cameraId)); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java index 408f2f88b26e..0890346db198 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java @@ -244,7 +244,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase { mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId, clientPackageName, clientAttributionTag, ICameraService.USE_CALLING_UID, - /*oomScoreOffset*/0); + /*oomScoreOffset*/0, getContext().getApplicationInfo().targetSdkVersion); assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser); mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); @@ -416,7 +416,8 @@ public class CameraDeviceBinderTest extends AndroidTestCase { @SmallTest public void testCameraCharacteristics() throws RemoteException { - CameraMetadataNative info = mUtils.getCameraService().getCameraCharacteristics(mCameraId); + CameraMetadataNative info = mUtils.getCameraService().getCameraCharacteristics(mCameraId, + getContext().getApplicationInfo().targetSdkVersion); assertFalse(info.isEmpty()); assertNotNull(info.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)); diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk Binary files differindex bb6dfa3a4ede..da4ae568d713 100644 --- a/packages/CtsShim/apk/arm/CtsShim.apk +++ b/packages/CtsShim/apk/arm/CtsShim.apk diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk Binary files differindex 2835d57474d9..214d5c2465da 100644 --- a/packages/CtsShim/apk/arm/CtsShimPriv.apk +++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk Binary files differindex bb6dfa3a4ede..da4ae568d713 100644 --- a/packages/CtsShim/apk/x86/CtsShim.apk +++ b/packages/CtsShim/apk/x86/CtsShim.apk diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk Binary files differindex 2e1a78989b70..b4c625f36510 100644 --- a/packages/CtsShim/apk/x86/CtsShimPriv.apk +++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml index dab3fcb02c04..a782fd25b6e8 100644 --- a/packages/PackageInstaller/res/values-de/strings.xml +++ b/packages/PackageInstaller/res/values-de/strings.xml @@ -46,7 +46,7 @@ <string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g> konnte nicht installiert werden. Gib Speicherplatz frei und versuche es noch einmal."</string> <string name="app_not_found_dlg_title" msgid="5107924008597470285">"App nicht gefunden"</string> <string name="app_not_found_dlg_text" msgid="5219983779377811611">"Die App wurde nicht in der Liste der installierten Apps gefunden."</string> - <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Nicht zulässig"</string> + <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Nicht zugelassen"</string> <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Der aktuelle Nutzer ist nicht dazu berechtigt, diese Deinstallation auszuführen."</string> <string name="generic_error_dlg_title" msgid="5863195085927067752">"Fehler"</string> <string name="generic_error_dlg_text" msgid="5287861443265795232">"App konnte nicht deinstalliert werden."</string> diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java index 42c1997b41a3..bfc00bb8b94d 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java @@ -52,6 +52,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.util.Arrays; +import java.util.NoSuchElementException; public final class RemotePrintDocument { private static final String LOG_TAG = "RemotePrintDocument"; @@ -441,7 +442,12 @@ public final class RemotePrintDocument { // Keep going - best effort... } - mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0); + try { + mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0); + } catch (NoSuchElementException e) { + Log.w(LOG_TAG, "Error unlinking print document adapter death recipient."); + // Keep going - best effort... + } } private void scheduleCommand(AsyncCommand command) { diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index 59f272ff70f6..d25d5dcaac87 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -61,7 +61,6 @@ import android.print.PrinterId; import android.print.PrinterInfo; import android.printservice.PrintService; import android.printservice.PrintServiceInfo; -import android.provider.DocumentsContract; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp index 51c9c39cdf03..b6e167717023 100644 --- a/packages/SettingsLib/ActionButtonsPreference/Android.bp +++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp @@ -14,7 +14,8 @@ android_library { resource_dirs: ["res"], static_libs: [ - "androidx.preference_preference", + "androidx.preference_preference", + "SettingsLibUtils", ], sdk_version: "system_current", diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java index 9aa511d610f2..029c9196fa5e 100644 --- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java +++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java @@ -27,10 +27,11 @@ import android.widget.Button; import androidx.annotation.DrawableRes; import androidx.annotation.StringRes; -import androidx.core.os.BuildCompat; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; +import com.android.settingslib.utils.BuildCompatUtils; + import java.util.ArrayList; import java.util.List; @@ -94,7 +95,7 @@ public class ActionButtonsPreference extends Preference { public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); - final boolean allowedDivider = !BuildCompat.isAtLeastS(); + final boolean allowedDivider = !BuildCompatUtils.isAtLeastS(); holder.setDividerAllowedAbove(allowedDivider); holder.setDividerAllowedBelow(allowedDivider); diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp index c6a95625a414..0f7a451e7c8b 100644 --- a/packages/SettingsLib/BannerMessagePreference/Android.bp +++ b/packages/SettingsLib/BannerMessagePreference/Android.bp @@ -16,6 +16,7 @@ android_library { static_libs: [ "androidx.preference_preference", "SettingsLibSettingsTheme", + "SettingsLibUtils", ], sdk_version: "system_current", diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml index 904b78c7f165..6f504efbaaf0 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml @@ -15,7 +15,7 @@ limitations under the License. --> -<LinearLayout +<com.android.settingslib.widget.BannerMessageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -23,6 +23,7 @@ style="@style/Banner.Preference.SettingsLib"> <RelativeLayout + android:id="@+id/top_row" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="8dp" @@ -49,6 +50,7 @@ android:id="@+id/banner_title" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:paddingTop="0dp" android:paddingBottom="4dp" android:textAppearance="@style/Banner.Title.SettingsLib"/> @@ -56,6 +58,7 @@ android:id="@+id/banner_subtitle" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:paddingTop="0dp" android:paddingBottom="4dp" android:textAppearance="@style/Banner.Subtitle.SettingsLib" android:visibility="gone"/> @@ -87,4 +90,4 @@ android:layout_height="wrap_content" style="@style/Banner.ButtonText.SettingsLib"/> </LinearLayout> -</LinearLayout>
\ No newline at end of file +</com.android.settingslib.widget.BannerMessageView>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml new file mode 100644 index 000000000000..d7e778ff854b --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Weier"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml new file mode 100644 index 000000000000..6701dea718ce --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"አሰናብት"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml new file mode 100644 index 000000000000..0f1b9acbd15b --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"إغلاق"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml new file mode 100644 index 000000000000..21dd94c66e18 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"অগ্ৰাহ্য কৰক"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml new file mode 100644 index 000000000000..7f91eb49db71 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Qapadın"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml new file mode 100644 index 000000000000..ca16c3de21db --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacite"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml new file mode 100644 index 000000000000..b0980ea12641 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Адхіліць"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml new file mode 100644 index 000000000000..cccbf964e53c --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отхвърляне"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml new file mode 100644 index 000000000000..e0dfcf24700c --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"বাতিল করুন"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml new file mode 100644 index 000000000000..5e46c6c28820 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacivanje"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml new file mode 100644 index 000000000000..81bb04803aea --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml new file mode 100644 index 000000000000..ac7623ecbfa4 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavřít"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml new file mode 100644 index 000000000000..8c185d97fede --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Luk"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml new file mode 100644 index 000000000000..006301b2e94e --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Schließen"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml new file mode 100644 index 000000000000..65843b282770 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Παράβλεψη"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml new file mode 100644 index 000000000000..418c1d59e6e7 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml new file mode 100644 index 000000000000..418c1d59e6e7 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml new file mode 100644 index 000000000000..418c1d59e6e7 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml new file mode 100644 index 000000000000..418c1d59e6e7 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml new file mode 100644 index 000000000000..e2dae5ec9bfb --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml new file mode 100644 index 000000000000..4816be6d6921 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Descartar"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml new file mode 100644 index 000000000000..5e820238c259 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cerrar"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml new file mode 100644 index 000000000000..a688723554bd --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Loobu"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml new file mode 100644 index 000000000000..64dd1c5d52db --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Baztertu"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml new file mode 100644 index 000000000000..bd8985f81d35 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"رد شدن"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml new file mode 100644 index 000000000000..c3841576b140 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ohita"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml new file mode 100644 index 000000000000..dd5889cbf983 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml new file mode 100644 index 000000000000..dd5889cbf983 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml new file mode 100644 index 000000000000..d7876261ebdd --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml new file mode 100644 index 000000000000..f66ee7fed130 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"खारिज करें"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml new file mode 100644 index 000000000000..f7e7cd0d930f --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbaci"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml new file mode 100644 index 000000000000..1551c843bab4 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Bezárás"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml new file mode 100644 index 000000000000..e014cce62eca --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Փակել"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml new file mode 100644 index 000000000000..607e8117515f --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Tutup"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml new file mode 100644 index 000000000000..4afc614c5a11 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hunsa"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml new file mode 100644 index 000000000000..81bb04803aea --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml new file mode 100644 index 000000000000..aa4c669c8e2c --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"סגירה"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml new file mode 100644 index 000000000000..b42f6e683dd9 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"閉じる"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml new file mode 100644 index 000000000000..7bde8b6dd0f8 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"უარყოფა"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml new file mode 100644 index 000000000000..01235e09182b --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабу"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml new file mode 100644 index 000000000000..4e14820be7c7 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ច្រានចោល"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml new file mode 100644 index 000000000000..b9a542045794 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ವಜಾಗೊಳಿಸಿ"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml new file mode 100644 index 000000000000..9b5169976a64 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"닫기"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml new file mode 100644 index 000000000000..affb8ec93fdd --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабуу"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml new file mode 100644 index 000000000000..7079f7c73ccb --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ປິດໄວ້"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml new file mode 100644 index 000000000000..4cee14a440ee --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Atsisakyti"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml new file mode 100644 index 000000000000..120a76286a90 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Nerādīt"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml new file mode 100644 index 000000000000..76a4390a6b72 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отфрли"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml new file mode 100644 index 000000000000..5a4e14c88c36 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ഡിസ്മിസ് ചെയ്യുക"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml new file mode 100644 index 000000000000..3974470813e4 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Үл хэрэгсэх"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml new file mode 100644 index 000000000000..4bd44859ec8f --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"डिसमिस करा"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml new file mode 100644 index 000000000000..290323be8392 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ketepikan"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml new file mode 100644 index 000000000000..52ecc4940c7d --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ပယ်ရန်"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml new file mode 100644 index 000000000000..c1e39a417a87 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Lukk"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml new file mode 100644 index 000000000000..15102541bb87 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"हटाउनुहोस्"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml new file mode 100644 index 000000000000..920349ff6fd4 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Sluiten"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml new file mode 100644 index 000000000000..36e7d3bdb216 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ଖାରଜ କରନ୍ତୁ"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml new file mode 100644 index 000000000000..250ef2e86aca --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ਖਾਰਜ ਕਰੋ"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml new file mode 100644 index 000000000000..9ad630adc92d --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zamknij"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml new file mode 100644 index 000000000000..80b70ae95655 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml new file mode 100644 index 000000000000..d7876261ebdd --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml new file mode 100644 index 000000000000..80b70ae95655 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml new file mode 100644 index 000000000000..18b6a0e4c830 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Respingeți"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml new file mode 100644 index 000000000000..b6946572c400 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрыть"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml new file mode 100644 index 000000000000..d818cf7db236 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ඉවත ලන්න"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml new file mode 100644 index 000000000000..4f59f851e574 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavrieť"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml new file mode 100644 index 000000000000..1ca68bf0709c --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Opusti"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml new file mode 100644 index 000000000000..dbe792759608 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hiq"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml new file mode 100644 index 000000000000..68a2d5bbf343 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Одбаците"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml new file mode 100644 index 000000000000..ef2df3ca45ea --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorera"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml new file mode 100644 index 000000000000..ebb0c0228b3a --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ondoa"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml new file mode 100644 index 000000000000..9b175c7b92c4 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"மூடும்"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml new file mode 100644 index 000000000000..6546bfa57ee8 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ปิด"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml new file mode 100644 index 000000000000..9b944de688ef --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"I-dismiss"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml new file mode 100644 index 000000000000..96d49e941cde --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Kapat"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml new file mode 100644 index 000000000000..f51b0e75f9ea --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрити"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml new file mode 100644 index 000000000000..ad3fafb14f3a --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"برخاست کریں"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml new file mode 100644 index 000000000000..1e247457e554 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Yopish"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml new file mode 100644 index 000000000000..a30cdbf1829f --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Đóng"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml new file mode 100644 index 000000000000..a8f36e4db962 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"关闭"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml new file mode 100644 index 000000000000..b9ee658dd4fa --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml new file mode 100644 index 000000000000..b9ee658dd4fa --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml new file mode 100644 index 000000000000..80faa175920b --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cashisa"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java index 9e39355e452d..3a70d657f62f 100644 --- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -35,10 +35,11 @@ import androidx.annotation.ColorInt; import androidx.annotation.ColorRes; import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; -import androidx.core.os.BuildCompat; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; +import com.android.settingslib.utils.BuildCompatUtils; + /** * Banner message is a banner displaying important information (permission request, page error etc), * and provide actions for user to address. It requires a user action to be dismissed. @@ -83,7 +84,7 @@ public class BannerMessagePreference extends Preference { } private static final String TAG = "BannerPreference"; - private static final boolean IS_AT_LEAST_S = BuildCompat.isAtLeastS(); + private static final boolean IS_AT_LEAST_S = BuildCompatUtils.isAtLeastS(); private final BannerMessagePreference.ButtonInfo mPositiveButtonInfo = new BannerMessagePreference.ButtonInfo(); diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java new file mode 100644 index 000000000000..5ca6fb64db2d --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.TouchDelegate; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.annotation.Nullable; + +/** + * The view providing {@link BannerMessagePreference}. + * + * <p>Callers should not instantiate this view directly but rather through adding a + * {@link BannerMessagePreference} to a {@code PreferenceScreen}. + */ +public class BannerMessageView extends LinearLayout { + private Rect mTouchTargetForDismissButton; + + public BannerMessageView(Context context) { + super(context); + } + + public BannerMessageView(Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public BannerMessageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public BannerMessageView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + setupIncreaseTouchTargetForDismissButton(); + } + + private void setupIncreaseTouchTargetForDismissButton() { + if (mTouchTargetForDismissButton != null) { + // Already set up + return; + } + + // The dismiss button is in the 'top row' RelativeLayout for positioning, but this element + // does not have enough space to provide large touch targets. We therefore set the top + // target on this view. + View topRow = findViewById(R.id.top_row); + View dismissButton = findViewById(R.id.banner_dismiss_btn); + if (topRow == null || dismissButton == null || dismissButton.getVisibility() != VISIBLE) { + return; + } + + int minimum = + getResources() + .getDimensionPixelSize(R.dimen.settingslib_preferred_minimum_touch_target); + int width = dismissButton.getWidth(); + int height = dismissButton.getHeight(); + int widthIncrease = width < minimum ? minimum - width : 0; + int heightIncrease = height < minimum ? minimum - height : 0; + + // Compute the hit rect of dismissButton within the local co-orindate reference of this view + // (rather than it's direct parent topRow). + Rect hitRectWithinTopRow = new Rect(); + dismissButton.getHitRect(hitRectWithinTopRow); + Rect hitRectOfTopRowWithinThis = new Rect(); + topRow.getHitRect(hitRectOfTopRowWithinThis); + mTouchTargetForDismissButton = new Rect(); + mTouchTargetForDismissButton.left = + hitRectOfTopRowWithinThis.left + hitRectWithinTopRow.left; + mTouchTargetForDismissButton.right = + hitRectOfTopRowWithinThis.left + hitRectWithinTopRow.right; + mTouchTargetForDismissButton.top = + hitRectOfTopRowWithinThis.top + hitRectWithinTopRow.top; + mTouchTargetForDismissButton.bottom = + hitRectOfTopRowWithinThis.top + hitRectWithinTopRow.bottom; + + // Adjust the touch target rect to apply the necessary increase in width and height. + mTouchTargetForDismissButton.left -= + widthIncrease % 2 == 1 ? (widthIncrease / 2) + 1 : widthIncrease / 2; + mTouchTargetForDismissButton.top -= + heightIncrease % 2 == 1 ? (heightIncrease / 2) + 1 : heightIncrease / 2; + mTouchTargetForDismissButton.right += widthIncrease / 2; + mTouchTargetForDismissButton.bottom += heightIncrease / 2; + + setTouchDelegate(new TouchDelegate(mTouchTargetForDismissButton, dismissButton)); + } + +} diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml index 55de3775bcad..7d9b4d78ce2a 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml @@ -21,8 +21,7 @@ android:id="@+id/content_parent" android:layout_width="match_parent" android:layout_height="match_parent" - android:fitsSystemWindows="true" - android:transitionGroup="true"> + android:fitsSystemWindows="true"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar" diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java index e6ca2e0d10c8..4ae120ae286d 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java @@ -23,8 +23,11 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toolbar; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.resources.TextAppearanceConfig; @@ -35,6 +38,7 @@ import com.google.android.material.resources.TextAppearanceConfig; public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { private CollapsingToolbarLayout mCollapsingToolbarLayout; + private AppBarLayout mAppBarLayout; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -43,6 +47,8 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { TextAppearanceConfig.setShouldLoadFontSynchronously(true); super.setContentView(R.layout.collapsing_toolbar_base_layout); mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar); + mAppBarLayout = findViewById(R.id.app_bar); + disableCollapsingToolbarLayoutScrollingBehavior(); final Toolbar toolbar = findViewById(R.id.action_bar); setActionBar(toolbar); @@ -107,4 +113,18 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { public CollapsingToolbarLayout getCollapsingToolbarLayout() { return mCollapsingToolbarLayout; } + + private void disableCollapsingToolbarLayoutScrollingBehavior() { + final CoordinatorLayout.LayoutParams params = + (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams(); + final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior(); + behavior.setDragCallback( + new AppBarLayout.Behavior.DragCallback() { + @Override + public boolean canDrag(@NonNull AppBarLayout appBarLayout) { + return false; + } + }); + params.setBehavior(behavior); + } } diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java index 8c2621d3e4ec..3a7fe3b92e78 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java @@ -16,22 +16,8 @@ package com.android.settingslib.collapsingtoolbar; -import static com.android.settingslib.transition.SettingsTransitionHelper.EXTRA_PAGE_TRANSITION_TYPE; - -import android.app.ActivityOptions; -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; -import android.view.Window; -import android.widget.Toolbar; - -import androidx.annotation.Nullable; -import androidx.core.os.BuildCompat; import androidx.fragment.app.FragmentActivity; -import com.android.settingslib.transition.SettingsTransitionHelper; -import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType; - /** * A base Activity for Settings-specific page transition. Activities extending it will get * Settings transition applied. @@ -39,57 +25,7 @@ import com.android.settingslib.transition.SettingsTransitionHelper.TransitionTyp public abstract class SettingsTransitionActivity extends FragmentActivity { private static final String TAG = "SettingsTransitionActivity"; - private Toolbar mToolbar; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - if (isSettingsTransitionEnabled()) { - getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS); - SettingsTransitionHelper.applyForwardTransition(this); - SettingsTransitionHelper.applyBackwardTransition(this); - } - - super.onCreate(savedInstanceState); - } - - @Override - public void setActionBar(@Nullable Toolbar toolbar) { - super.setActionBar(toolbar); - - mToolbar = toolbar; - } - - @Override - public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { - final int transitionType = intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE, - TransitionType.TRANSITION_SHARED_AXIS); - if (!isSettingsTransitionEnabled() || - transitionType == TransitionType.TRANSITION_NONE) { - super.startActivityForResult(intent, requestCode, options); - return; - } - - super.startActivityForResult(intent, requestCode, - createActivityOptionsBundleForTransition(options)); - } - protected boolean isSettingsTransitionEnabled() { - return BuildCompat.isAtLeastS(); - } - - @Nullable - private Bundle createActivityOptionsBundleForTransition(@Nullable Bundle options) { - if (mToolbar == null) { - Log.w(TAG, "setActionBar(Toolbar) is not called. Cannot apply settings transition!"); - return options; - } - final Bundle transitionOptions = ActivityOptions.makeSceneTransitionAnimation(this, - mToolbar, "shared_element_view").toBundle(); - if (options == null) { - return transitionOptions; - } - final Bundle mergedOptions = new Bundle(options); - mergedOptions.putAll(transitionOptions); - return mergedOptions; + return false; } } diff --git a/packages/SettingsLib/FooterPreference/res/drawable/ic_info_outline_24.xml b/packages/SettingsLib/FooterPreference/res/drawable/settingslib_ic_info_outline_24.xml index d9afeb0a6361..d9afeb0a6361 100644 --- a/packages/SettingsLib/FooterPreference/res/drawable/ic_info_outline_24.xml +++ b/packages/SettingsLib/FooterPreference/res/drawable/settingslib_ic_info_outline_24.xml diff --git a/packages/SettingsLib/FooterPreference/res/values-af/strings.xml b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml new file mode 100644 index 000000000000..c17f3edc6744 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Kom meer te wete"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-am/strings.xml b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml new file mode 100644 index 000000000000..02e61312d77c --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"የበለጠ ለመረዳት"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml new file mode 100644 index 000000000000..1f279a6a9f72 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"مزيد من المعلومات"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-as/strings.xml b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml new file mode 100644 index 000000000000..a34b474edac2 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"অধিক জানক"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-az/strings.xml b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml new file mode 100644 index 000000000000..b49036ee0bbc --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ətraflı məlumat"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml new file mode 100644 index 000000000000..993ec9a92182 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-be/strings.xml b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml new file mode 100644 index 000000000000..f9d6129ba1f6 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Даведацца больш"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml new file mode 100644 index 000000000000..605663d33c0c --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Научете повече"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml new file mode 100644 index 000000000000..c58142d72618 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"আরও জানুন"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml new file mode 100644 index 000000000000..993ec9a92182 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml new file mode 100644 index 000000000000..7abf10f24957 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Més informació"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml new file mode 100644 index 000000000000..decbb68efe5d --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Další informace"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-da/strings.xml b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml new file mode 100644 index 000000000000..81d1c7cf5d04 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Få flere oplysninger"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-de/strings.xml b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml new file mode 100644 index 000000000000..fe885aa6f091 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Weitere Informationen"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-el/strings.xml b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml new file mode 100644 index 000000000000..5a30833222ad --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Μάθετε περισσότερα"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml new file mode 100644 index 000000000000..924d735d4d6a --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml new file mode 100644 index 000000000000..924d735d4d6a --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml new file mode 100644 index 000000000000..924d735d4d6a --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml new file mode 100644 index 000000000000..924d735d4d6a --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml new file mode 100644 index 000000000000..bd12547ad6ff --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml new file mode 100644 index 000000000000..f31d9ea26005 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-es/strings.xml b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml new file mode 100644 index 000000000000..f31d9ea26005 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-et/strings.xml b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml new file mode 100644 index 000000000000..78b65edd9e57 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lisateave"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml new file mode 100644 index 000000000000..cf7fa003ca28 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lortu informazio gehiago"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml new file mode 100644 index 000000000000..464c58e02477 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"بیشتر بدانید"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml new file mode 100644 index 000000000000..856b96288cda --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lue lisää"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml new file mode 100644 index 000000000000..6d856caab1ea --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml new file mode 100644 index 000000000000..6d856caab1ea --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml new file mode 100644 index 000000000000..cde57d868361 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Máis información"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml new file mode 100644 index 000000000000..95ae240d1ea0 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ज़्यादा जानें"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml new file mode 100644 index 000000000000..993ec9a92182 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml new file mode 100644 index 000000000000..ae3c94809f7c --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"További információ"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml new file mode 100644 index 000000000000..de9137b0f241 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Իմանալ ավելին"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-in/strings.xml b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml new file mode 100644 index 000000000000..4b5cb16f95ca --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Pelajari lebih lanjut"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-is/strings.xml b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml new file mode 100644 index 000000000000..111094c201cd --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Nánar"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-it/strings.xml b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml new file mode 100644 index 000000000000..053c80c6c4ea --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Scopri di più"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml new file mode 100644 index 000000000000..55b01873c2fc --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"מידע נוסף"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml new file mode 100644 index 000000000000..3312cb430ed7 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"詳細"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml new file mode 100644 index 000000000000..67bb223f68ec --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"შეიტყვეთ მეტი"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml new file mode 100644 index 000000000000..db11a766e8a5 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Толығырақ"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-km/strings.xml b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml new file mode 100644 index 000000000000..1977dd323b20 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ស្វែងយល់បន្ថែម"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml new file mode 100644 index 000000000000..47fa3d5482ba --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml new file mode 100644 index 000000000000..d8d220068c03 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"자세히 알아보기"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml new file mode 100644 index 000000000000..74c6a497e3d0 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Кеңири маалымат"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml new file mode 100644 index 000000000000..2e4124b8eb42 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ສຶກສາເພີ່ມເຕີມ"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml new file mode 100644 index 000000000000..2981c665abfe --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Sužinokite daugiau"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml new file mode 100644 index 000000000000..97663056591f --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Uzzināt vairāk"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml new file mode 100644 index 000000000000..1f734c5a12a4 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Дознајте повеќе"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml new file mode 100644 index 000000000000..1cd466bcce34 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"കൂടുതലറിയുക"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml new file mode 100644 index 000000000000..8bac1eb110a0 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Нэмэлт мэдээлэл авах"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml new file mode 100644 index 000000000000..45387200bdc8 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"अधिक जाणून घ्या"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml new file mode 100644 index 000000000000..cd1b17a96ea4 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ketahui lebih lanjut"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-my/strings.xml b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml new file mode 100644 index 000000000000..751a87a33da2 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ပိုမိုလေ့လာရန်"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml new file mode 100644 index 000000000000..08de00943699 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Finn ut mer"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml new file mode 100644 index 000000000000..ecfec36337ee --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"थप जान्नुहोस्"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml new file mode 100644 index 000000000000..156408135ad1 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Meer informatie"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-or/strings.xml b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml new file mode 100644 index 000000000000..e7924d646861 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ଅଧିକ ଜାଣନ୍ତୁ"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml new file mode 100644 index 000000000000..1ce2ef246a36 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ਹੋਰ ਜਾਣੋ"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml new file mode 100644 index 000000000000..5709f3e8cdee --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Więcej informacji"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml new file mode 100644 index 000000000000..bc410ab8b62f --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml new file mode 100644 index 000000000000..bc410ab8b62f --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml new file mode 100644 index 000000000000..bc410ab8b62f --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml new file mode 100644 index 000000000000..2b5011764508 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Aflați mai multe"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml new file mode 100644 index 000000000000..bedde40f5ad7 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Подробнее…"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-si/strings.xml b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml new file mode 100644 index 000000000000..1a60601cf36c --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"තව දැන ගන්න"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml new file mode 100644 index 000000000000..c1008e5a433a --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ďalšie informácie"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml new file mode 100644 index 000000000000..79e0a735b927 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Več o tem"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml new file mode 100644 index 000000000000..0fea476cfbb8 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Mëso më shumë"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml new file mode 100644 index 000000000000..9a73269b9d0e --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Сазнајте више"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml new file mode 100644 index 000000000000..a78c3cbc6c0b --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Läs mer"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml new file mode 100644 index 000000000000..52b1732043a3 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Pata maelezo zaidi"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml new file mode 100644 index 000000000000..75fc7c1adff2 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"மேலும் அறிக"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-th/strings.xml b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml new file mode 100644 index 000000000000..025a2f0f3705 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ดูข้อมูลเพิ่มเติม"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml new file mode 100644 index 000000000000..4b6f830a4371 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Matuto pa"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml new file mode 100644 index 000000000000..77d151309c4c --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Daha fazla bilgi"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml new file mode 100644 index 000000000000..cec933d374a4 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Докладніше"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml new file mode 100644 index 000000000000..1dceea7739eb --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"مزید جانیں"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml new file mode 100644 index 000000000000..58239498256a --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Batafsil"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml new file mode 100644 index 000000000000..d6c46389fdfa --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Tìm hiểu thêm"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml new file mode 100644 index 000000000000..446c8ce4bc4c --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"了解详情"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml new file mode 100644 index 000000000000..8ab38c658c8f --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml new file mode 100644 index 000000000000..8ab38c658c8f --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml new file mode 100644 index 000000000000..b53eb85428b4 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Funda kabanzi"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java index 005c75c45a46..8216edf872f3 100644 --- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java @@ -149,7 +149,7 @@ public class FooterPreference extends Preference { private void init() { setLayoutResource(R.layout.preference_footer); if (getIcon() == null) { - setIcon(R.drawable.ic_info_outline_24); + setIcon(R.drawable.settingslib_ic_info_outline_24); } setOrder(ORDER_FOOTER); if (TextUtils.isEmpty(getKey())) { diff --git a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml index 120b0859a70a..73163fca5362 100644 --- a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml +++ b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml @@ -18,6 +18,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.settingslib.widget"> - <uses-sdk android:minSdkVersion="21" /> + <uses-sdk android:minSdkVersion="28" /> </manifest> diff --git a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml index 3f8439c2db47..efcd41c2778b 100644 --- a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml +++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml @@ -20,35 +20,38 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:background="?android:attr/colorBackground" + android:importantForAccessibility="noHideDescendants" android:gravity="center" android:orientation="horizontal"> - <LinearLayout - android:layout_width="match_parent" + <FrameLayout + android:id="@+id/illustration_frame" + android:layout_width="wrap_content" android:layout_height="@dimen/settingslib_illustration_height" android:layout_gravity="center" android:gravity="center_vertical" android:padding="@dimen/settingslib_illustration_padding" android:orientation="vertical"> + + <View + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/protection_background"/> + <com.airbnb.lottie.LottieAnimationView android:id="@+id/lottie_view" - android:adjustViewBounds="true" - android:maxWidth="@dimen/settingslib_illustration_width" - android:layout_width="wrap_content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" /> + + <FrameLayout + android:id="@+id/middleground_layout" + android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@android:color/transparent" android:layout_gravity="center" - android:clipToOutline="true" - android:background="@drawable/protection_background" - android:importantForAccessibility="no"/> - </LinearLayout> + android:visibility="gone"/> + </FrameLayout> - <FrameLayout - android:id="@+id/middleground_layout" - android:layout_width="@dimen/settingslib_illustration_width" - android:layout_height="@dimen/settingslib_illustration_height" - android:padding="@dimen/settingslib_illustration_padding" - android:background="@android:color/transparent" - android:layout_gravity="center" - android:visibility="gone"/> </FrameLayout> diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java index cd2ddebe6367..07102d55ef7e 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java @@ -39,13 +39,6 @@ import java.util.HashMap; public class ColorUtils { private static HashMap<String, Integer> sSysColors; - static { - sSysColors = new HashMap<>(); - sSysColors.put(".colorAccent", android.R.attr.colorAccent); - sSysColors.put(".colorPrimary", android.R.attr.colorPrimary); - sSysColors.put(".colorPrimaryDark", android.R.attr.colorPrimaryDark); - sSysColors.put(".colorSecondary", android.R.attr.colorSecondary); - } private static HashMap<String, Pair<Integer, Integer>> sFixedColors; static { @@ -110,19 +103,6 @@ public class ColorUtils { * Apply the color of tags to the animation. */ public static void applyDynamicColors(Context context, LottieAnimationView animationView) { - for (String key : sSysColors.keySet()) { - final int color = sSysColors.get(key); - animationView.addValueCallback( - new KeyPath("**", key, "**"), - LottieProperty.COLOR_FILTER, - new SimpleLottieValueCallback<ColorFilter>() { - @Override - public ColorFilter getValue(LottieFrameInfo<ColorFilter> frameInfo) { - return new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN); - } - } - ); - } for (String key : sFixedColors.keySet()) { final Pair<Integer, Integer> fixedColorPair = sFixedColors.get(key); final int color = isDarkMode(context) ? fixedColorPair.second : fixedColorPair.first; diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java index 1370eef2743b..e91dd94c715d 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java @@ -21,6 +21,7 @@ import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.Log; import android.view.View; +import android.view.ViewGroup.LayoutParams; import android.widget.FrameLayout; import android.widget.ImageView; @@ -35,7 +36,9 @@ import com.airbnb.lottie.LottieAnimationView; */ public class IllustrationPreference extends Preference { - static final String TAG = "IllustrationPreference"; + private static final String TAG = "IllustrationPreference"; + + private static final boolean IS_ENABLED_LOTTIE_ADAPTIVE_COLOR = false; private int mAnimationId; private boolean mIsAutoScale; @@ -66,11 +69,22 @@ public class IllustrationPreference extends Preference { Log.w(TAG, "Invalid illustration resource id."); return; } + + // To solve the problem of non-compliant illustrations, we set the frame height + // to 300dp and set the length of the short side of the screen to + // the width of the frame. + final int screenWidth = getContext().getResources().getDisplayMetrics().widthPixels; + final int screenHeight = getContext().getResources().getDisplayMetrics().heightPixels; + final FrameLayout illustrationFrame = (FrameLayout) holder.findViewById( + R.id.illustration_frame); + final LayoutParams lp = (LayoutParams) illustrationFrame.getLayoutParams(); + lp.width = screenWidth < screenHeight ? screenWidth : screenHeight; + illustrationFrame.setLayoutParams(lp); + mMiddleGroundLayout = (FrameLayout) holder.findViewById(R.id.middleground_layout); mIllustrationView = (LottieAnimationView) holder.findViewById(R.id.lottie_view); mIllustrationView.setAnimation(mAnimationId); mIllustrationView.loop(true); - ColorUtils.applyDynamicColors(getContext(), mIllustrationView); mIllustrationView.playAnimation(); if (mIsAutoScale) { enableAnimationAutoScale(mIsAutoScale); @@ -78,6 +92,9 @@ public class IllustrationPreference extends Preference { if (mMiddleGroundView != null) { enableMiddleGroundView(); } + if (IS_ENABLED_LOTTIE_ADAPTIVE_COLOR) { + ColorUtils.applyDynamicColors(getContext(), mIllustrationView); + } } @VisibleForTesting @@ -120,6 +137,13 @@ public class IllustrationPreference extends Preference { mIsAutoScale ? ImageView.ScaleType.CENTER_CROP : ImageView.ScaleType.CENTER_INSIDE); } + /** + * Set the lottie illustration resource id. + */ + public void setLottieAnimationResId(int resId) { + mAnimationId = resId; + } + private void enableMiddleGroundView() { mMiddleGroundLayout.removeAllViews(); mMiddleGroundLayout.addView(mMiddleGroundView); diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp index 23ee49e70595..76d1ea78258d 100644 --- a/packages/SettingsLib/MainSwitchPreference/Android.bp +++ b/packages/SettingsLib/MainSwitchPreference/Android.bp @@ -16,8 +16,9 @@ android_library { static_libs: [ "androidx.preference_preference", "SettingsLibSettingsTheme", + "SettingsLibUtils", ], sdk_version: "system_current", - min_sdk_version: "21", + min_sdk_version: "28", } diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml index 26075295c155..5817f77eb6d9 100644 --- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml +++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml @@ -19,7 +19,7 @@ package="com.android.settingslib.widget"> <uses-sdk - android:minSdkVersion="21" + android:minSdkVersion="28" android:targetSdkVersion="31"/> </manifest> diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml index 0c95a9ec65e1..57b8446e2530 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml @@ -16,7 +16,6 @@ --> <resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> - <color name="settingslib_switchbar_background_color">@*android:color/material_grey_600</color> <color name="settingslib_switchbar_switch_track_tint">#BFFFFFFF</color> <color name="settingslib_switchbar_switch_thumb_tint">@android:color/white</color> </resources> diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java index 91e1b9319f28..5f47be4b0642 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java @@ -30,7 +30,8 @@ import android.widget.Switch; import android.widget.TextView; import androidx.annotation.ColorInt; -import androidx.core.os.BuildCompat; + +import com.android.settingslib.utils.BuildCompatUtils; import java.util.ArrayList; import java.util.List; @@ -74,11 +75,11 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this); - if (!BuildCompat.isAtLeastS()) { + if (!BuildCompatUtils.isAtLeastS()) { final TypedArray a = context.obtainStyledAttributes( new int[]{android.R.attr.colorAccent}); mBackgroundActivatedColor = a.getColor(0, 0); - mBackgroundColor = context.getColor(R.color.settingslib_switchbar_background_color); + mBackgroundColor = context.getColor(R.color.material_grey_600); a.recycle(); } @@ -88,11 +89,12 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec mFrameView = findViewById(R.id.frame); mTextView = (TextView) findViewById(R.id.switch_text); mSwitch = (Switch) findViewById(android.R.id.switch_widget); - mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on); - mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off); - mBackgroundDisabled = getContext().getDrawable( - R.drawable.settingslib_switch_bar_bg_disabled); - + if (BuildCompatUtils.isAtLeastS()) { + mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on); + mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off); + mBackgroundDisabled = getContext().getDrawable( + R.drawable.settingslib_switch_bar_bg_disabled); + } addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked)); setChecked(mSwitch.isChecked()); @@ -194,9 +196,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec * Remove a listener for switch changes */ public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) { - if (mSwitchChangeListeners.contains(listener)) { - mSwitchChangeListeners.remove(listener); - } + mSwitchChangeListeners.remove(listener); } /** @@ -207,7 +207,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec mTextView.setEnabled(enabled); mSwitch.setEnabled(enabled); - if (BuildCompat.isAtLeastS()) { + if (BuildCompatUtils.isAtLeastS()) { if (enabled) { mFrameView.setBackground(isChecked() ? mBackgroundOn : mBackgroundOff); } else { @@ -226,7 +226,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec } private void setBackground(boolean isChecked) { - if (!BuildCompat.isAtLeastS()) { + if (!BuildCompatUtils.isAtLeastS()) { setBackgroundColor(isChecked ? mBackgroundActivatedColor : mBackgroundColor); } else { mFrameView.setBackground(isChecked ? mBackgroundOn : mBackgroundOff); @@ -267,10 +267,12 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { + @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } + @Override public SavedState[] newArray(int size) { return new SavedState[size]; } diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml index b299061ce591..906ff2cc09d5 100644 --- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml +++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml @@ -104,7 +104,7 @@ android:gravity="center_vertical"> <View android:layout_width=".75dp" - android:layout_height="match_parent" + android:layout_height="32dp" android:layout_marginTop="16dp" android:layout_marginBottom="16dp" android:background="?android:attr/dividerVertical" /> diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java index 304c3439a60d..d993e4465343 100644 --- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java @@ -22,6 +22,7 @@ import android.view.View; import android.widget.AdapterView; import androidx.preference.Preference; +import androidx.preference.Preference.OnPreferenceClickListener; import androidx.preference.PreferenceViewHolder; import com.android.settingslib.widget.settingsspinner.SettingsSpinner; @@ -31,12 +32,12 @@ import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter; * This preference uses SettingsSpinner & SettingsSpinnerAdapter which provide default layouts for * both view and drop down view of the Spinner. */ -public class SettingsSpinnerPreference extends Preference { +public class SettingsSpinnerPreference extends Preference implements OnPreferenceClickListener { private SettingsSpinnerAdapter mAdapter; private AdapterView.OnItemSelectedListener mListener; - private int mPosition; //Default 0 for internal shard storage. - private boolean mIsClickable = true; + private int mPosition; + private boolean mShouldPerformClick; /** * Perform inflation from XML and apply a class-specific base style. @@ -51,7 +52,7 @@ public class SettingsSpinnerPreference extends Preference { public SettingsSpinnerPreference(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setLayoutResource(R.layout.settings_spinner_preference); - setSelectable(false); + setOnPreferenceClickListener(this); } /** @@ -64,7 +65,7 @@ public class SettingsSpinnerPreference extends Preference { public SettingsSpinnerPreference(Context context, AttributeSet attrs) { super(context, attrs); setLayoutResource(R.layout.settings_spinner_preference); - setSelectable(false); + setOnPreferenceClickListener(this); } /** @@ -76,6 +77,13 @@ public class SettingsSpinnerPreference extends Preference { this(context, null); } + @Override + public boolean onPreferenceClick(Preference preference) { + mShouldPerformClick = true; + notifyChanged(); + return true; + } + /** Sets adapter of the spinner. */ public <T extends SettingsSpinnerAdapter> void setAdapter(T adapter) { mAdapter = adapter; @@ -101,24 +109,19 @@ public class SettingsSpinnerPreference extends Preference { notifyChanged(); } - /** Set clickable of the spinner. */ - public void setClickable(boolean isClickable) { - if (mIsClickable == isClickable) { - return; - } - mIsClickable = isClickable; - notifyChanged(); - } @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); final SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.spinner); - spinner.setEnabled(mIsClickable); - spinner.setClickable(mIsClickable); spinner.setAdapter(mAdapter); spinner.setSelection(mPosition); spinner.setOnItemSelectedListener(mOnSelectedListener); + if (mShouldPerformClick) { + mShouldPerformClick = false; + // To show dropdown view. + spinner.performClick(); + } } private final AdapterView.OnItemSelectedListener mOnSelectedListener = diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml index aed6338495f0..b78da90ba239 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml @@ -34,7 +34,7 @@ <!-- Dialog accent color --> <color name="settingslib_dialog_accent">@android:color/system_accent1_600</color> <!-- Dialog background color --> - <color name="settingslib_dialog_background">@android:color/system_neutral1_800</color> + <color name="settingslib_dialog_background">@*android:color/surface_light</color> <!-- Dialog error color. --> <color name="settingslib_dialog_colorError">#d93025</color> <!-- Red 600 --> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml index acbf35946126..ddcc83eee4bf 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml @@ -18,4 +18,5 @@ <resources> <dimen name="app_preference_padding_start">20dp</dimen> <dimen name="app_icon_min_width">52dp</dimen> + <dimen name="settingslib_preferred_minimum_touch_target">48dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml index 6b18f2879659..8034710b4341 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml @@ -42,7 +42,7 @@ </style> <style name="Theme.AlertDialog.SettingsLib" parent="@style/Theme.AppCompat.DayNight.Dialog.Alert"> - <item name="colorAccent">@*android:color/accent_device_default_light</item> + <item name="colorAccent">@color/settingslib_dialog_accent</item> <item name="android:colorError">@color/settingslib_dialog_colorError</item> <item name="android:colorBackground">@color/settingslib_dialog_background</item> diff --git a/packages/SettingsLib/SettingsTransition/Android.bp b/packages/SettingsLib/SettingsTransition/Android.bp index d8cd556ba85d..f06a9a7c0f43 100644 --- a/packages/SettingsLib/SettingsTransition/Android.bp +++ b/packages/SettingsLib/SettingsTransition/Android.bp @@ -11,7 +11,6 @@ android_library { name: "SettingsLibSettingsTransition", srcs: ["src/**/*.java"], - resource_dirs: ["res"], static_libs: [ "com.google.android.material_material", diff --git a/packages/SettingsLib/SettingsTransition/res/values/dimens.xml b/packages/SettingsLib/SettingsTransition/res/values/dimens.xml deleted file mode 100644 index 0630ca85b621..000000000000 --- a/packages/SettingsLib/SettingsTransition/res/values/dimens.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2021 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources> - <dimen name="settings_shared_axis_x_slide_distance">96dp</dimen> -</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java index a6eeb8867346..540f8c2ce1f5 100644 --- a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java +++ b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java @@ -16,21 +16,11 @@ package com.android.settingslib.transition; -import androidx.annotation.IntDef; import android.app.Activity; -import android.content.Context; -import android.util.Log; -import android.view.Window; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; -import androidx.core.os.BuildCompat; +import androidx.annotation.IntDef; import androidx.fragment.app.Fragment; -import com.google.android.material.transition.platform.FadeThroughProvider; -import com.google.android.material.transition.platform.MaterialSharedAxis; -import com.google.android.material.transition.platform.SlideDistanceProvider; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -59,31 +49,6 @@ public class SettingsTransitionHelper { public static final String EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type"; private static final String TAG = "SettingsTransitionHelper"; - private static final long DURATION = 450L; - private static final float FADE_THROUGH_THRESHOLD = 0.22F; - - private static MaterialSharedAxis createSettingsSharedAxis(Context context, boolean forward) { - final MaterialSharedAxis transition = new MaterialSharedAxis(MaterialSharedAxis.X, forward); - transition.excludeTarget(android.R.id.statusBarBackground, true); - transition.excludeTarget(android.R.id.navigationBarBackground, true); - - final SlideDistanceProvider forwardDistanceProvider = - (SlideDistanceProvider) transition.getPrimaryAnimatorProvider(); - final int distance = context.getResources().getDimensionPixelSize( - R.dimen.settings_shared_axis_x_slide_distance); - forwardDistanceProvider.setSlideDistance(distance); - transition.setDuration(DURATION); - - final FadeThroughProvider fadeThroughProvider = - (FadeThroughProvider) transition.getSecondaryAnimatorProvider(); - fadeThroughProvider.setProgressThreshold(FADE_THROUGH_THRESHOLD); - - final Interpolator interpolator = - AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_extra_slow_in); - transition.setInterpolator(interpolator); - - return transition; - } /** * Apply the forward transition to the {@link Activity}, including Exit Transition and Enter @@ -92,23 +57,16 @@ public class SettingsTransitionHelper { * The Exit Transition takes effect when leaving the page, while the Enter Transition is * triggered when the page is launched/entering. */ - public static void applyForwardTransition(Activity activity) { - if (!isSettingsTransitionEnabled()) { - return; - } - if (activity == null) { - Log.w(TAG, "applyForwardTransition: Invalid activity!"); - return; - } - final Window window = activity.getWindow(); - if (window == null) { - Log.w(TAG, "applyForwardTransition: Invalid window!"); - return; - } - final MaterialSharedAxis forward = createSettingsSharedAxis(activity, true); - window.setExitTransition(forward); - window.setEnterTransition(forward); - } + public static void applyForwardTransition(Activity activity) {} + + /** + * Apply the forward transition to the {@link Fragment}, including Exit Transition and Enter + * Transition. + * + * The Exit Transition takes effect when leaving the page, while the Enter Transition is + * triggered when the page is launched/entering. + */ + public static void applyForwardTransition(Fragment fragment) {} /** * Apply the backward transition to the {@link Activity}, including Return Transition and @@ -118,43 +76,7 @@ public class SettingsTransitionHelper { * to close. Reenter Transition will be used to move Views in to the scene when returning from a * previously-started Activity. */ - public static void applyBackwardTransition(Activity activity) { - if (!isSettingsTransitionEnabled()) { - return; - } - if (activity == null) { - Log.w(TAG, "applyBackwardTransition: Invalid activity!"); - return; - } - final Window window = activity.getWindow(); - if (window == null) { - Log.w(TAG, "applyBackwardTransition: Invalid window!"); - return; - } - final MaterialSharedAxis backward = createSettingsSharedAxis(activity, false); - window.setReturnTransition(backward); - window.setReenterTransition(backward); - } - - /** - * Apply the forward transition to the {@link Fragment}, including Exit Transition and Enter - * Transition. - * - * The Exit Transition takes effect when leaving the page, while the Enter Transition is - * triggered when the page is launched/entering. - */ - public static void applyForwardTransition(Fragment fragment) { - if (!isSettingsTransitionEnabled()) { - return; - } - if (fragment == null) { - Log.w(TAG, "applyForwardTransition: Invalid fragment!"); - return; - } - final MaterialSharedAxis forward = createSettingsSharedAxis(fragment.getContext(), true); - fragment.setExitTransition(forward); - fragment.setEnterTransition(forward); - } + public static void applyBackwardTransition(Activity activity) {} /** * Apply the backward transition to the {@link Fragment}, including Return Transition and @@ -164,20 +86,5 @@ public class SettingsTransitionHelper { * to close. Reenter Transition will be used to move Views in to the scene when returning from a * previously-started Fragment. */ - public static void applyBackwardTransition(Fragment fragment) { - if (!isSettingsTransitionEnabled()) { - return; - } - if (fragment == null) { - Log.w(TAG, "applyBackwardTransition: Invalid fragment!"); - return; - } - final MaterialSharedAxis backward = createSettingsSharedAxis(fragment.getContext(), false); - fragment.setReturnTransition(backward); - fragment.setReenterTransition(backward); - } - - private static boolean isSettingsTransitionEnabled() { - return BuildCompat.isAtLeastS(); - } + public static void applyBackwardTransition(Fragment fragment) {} } diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml index 96d9e518663f..fd89676ab6d7 100644 --- a/packages/SettingsLib/Utils/AndroidManifest.xml +++ b/packages/SettingsLib/Utils/AndroidManifest.xml @@ -16,7 +16,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.settingslib.widget"> + package="com.android.settingslib.utils"> <uses-sdk android:minSdkVersion="21" /> diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java new file mode 100644 index 000000000000..44f6f5439af3 --- /dev/null +++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.utils; + +import android.os.Build.VERSION; + +/** + * An util class to check whether the current OS version is higher or equal to sdk version of + * device. + */ +public final class BuildCompatUtils { + + /** + * Implementation of BuildCompat.isAtLeast*() suitable for use in Settings + * + * <p>This still should try using BuildCompat.isAtLeastR() as source of truth, but also checking + * for VERSION_SDK_INT and VERSION.CODENAME in case when BuildCompat implementation returned + * false. Note that both checks should be >= and not = to make sure that when Android version + * increases (i.e., from R to S), this does not stop working. + * + * <p>Supported configurations: + * + * <ul> + * <li>For current Android release: when new API is not finalized yet (CODENAME = "S", SDK_INT + * = 30|31) + * <li>For current Android release: when new API is finalized (CODENAME = "REL", SDK_INT = 31) + * <li>For next Android release (CODENAME = "T", SDK_INT = 30+) + * </ul> + * + * <p>Note that Build.VERSION_CODES.S cannot be used here until final SDK is available, because + * it is equal to Build.VERSION_CODES.CUR_DEVELOPMENT before API finalization. + * + * @return Whether the current OS version is higher or equal to S. + */ + public static boolean isAtLeastS() { + if (VERSION.SDK_INT < 30) { + return false; + } + + return (VERSION.CODENAME.equals("REL") && VERSION.SDK_INT >= 31) + || (VERSION.CODENAME.length() == 1 + && VERSION.CODENAME.compareTo("S") >= 0 + && VERSION.CODENAME.compareTo("Z") <= 0); + } + + private BuildCompatUtils() {} +} diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java index 4505dad8ea12..5dc0b7274408 100644 --- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java +++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java @@ -22,7 +22,7 @@ import android.content.pm.PackageManager; import android.os.UserManager; import android.util.Log; -import com.android.settingslib.widget.R; +import com.android.settingslib.utils.R; public class AppUtils { diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 366cb0677e46..89bb9e8bfe01 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Bynaam"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gas"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 8a5da977717d..bc1bd1633e4c 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string> <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 0eef43b18a9a..525a4978f580 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -511,8 +511,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"المنبّهات والتذكيرات"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"السماح بضبط المنبّهات والتذكيرات"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"المنبّهات والتذكيرات"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"عليك السماح لهذا التطبيق بضبط المنبّهات وتحديد مواعيد للإجراءات الحساسة زمنيًا. يسمح هذا الأذن بتشغيل التطبيق في الخلفية، ما قد يستهلك المزيد من شحن البطارية.\n\nإذا كان هذا الإذن غير مسموح به، لن تعمل الأحداث المستندة إلى وقت والمنبّهات الحالية التي يحدِّد هذا التطبيق موعدها."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"جدول زمني، جدولة، منبّه، تذكير، ساعة"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"تفعيل"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"تفعيل ميزة \"عدم الإزعاج\""</string> @@ -571,6 +570,8 @@ <string name="user_nickname" msgid="262624187455825083">"اللقب"</string> <string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string> <string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index b8796fe107e7..f04aaac87b6d 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ ছেট কৰাৰ অনুমতি দিয়ক"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"এই এপ্টোক এলাৰ্ম ছেট কৰিবলৈ আৰু সময় সংবেদনশীল কাৰ্যৰ সময়সূচী নিৰ্ধাৰণ কৰিবলৈ দিয়ক। ই এপ্টোক নেপথ্যত চলি থকাৰ অনুমতি দিয়ে যাৰ ফলত অধিক বেটাৰী ব্যৱহাৰ হয়।\n\nএই অনুমতিটো অফ কৰা থাকিলে, ইতিমধ্যে ছেট কৰা এলাৰ্ম আৰু এই এপ্টোৱে সময়সূচী নিৰ্ধাৰণ কৰা সময় ভিত্তিক অনুষ্ঠানসমূহে কাম নকৰা হ’ব।"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"সময়সূচী, এলাৰ্ম, ৰিমাইণ্ডাৰ, ঘড়ী"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"অন কৰক"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"অসুবিধা নিদিব অন কৰক"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"উপনাম"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string> <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index a5a3321d821b..b7cf11ae3754 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 790323003ba1..97c281ad2d67 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -567,6 +567,8 @@ <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 0fb717898be6..2d1a3629992c 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -509,8 +509,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Будзільнікі і напаміны"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Дазволіць усталёўваць будзільнікі і напаміны"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будзільнікі і напаміны"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дазвольце гэтай праграме ўключаць будзільнікі і задаваць час дзеянняў. З такім дазволам праграма можа працаваць у фонавым рэжыме і ў выніку хутчэй разраджаць акумулятар.\n\nКалі вы не ўключыце гэты дазвол, існуючыя будзільнікі і запланаваны праграмай час падзей не будуць працаваць."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"расклад, будзільнік, напамін, гадзіннік"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Уключыць"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Уключэнне рэжыму \"Не турбаваць\""</string> @@ -569,6 +568,8 @@ <string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Госць"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 4d92282b62d6..09e584318173 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index b7fdef23ce67..26ad54a6c31e 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"অ্যালার্ম এবং রিমাইন্ডার"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"অ্যালার্ম এবং রিমাইন্ডার সেট করার অনুমতি দিন"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"অ্যালার্ম এবং রিমাইন্ডার"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"অ্যালার্ম এবং সময়ের মধ্যে শেষ করতে হবে এমন অ্যাকশনের শিডিউল সেট করতে এই অ্যাপকে অনুমতি দিন। এর ফলে ব্যাকগ্রাউন্ডে অ্যাপ চলতে পারে, যার জন্য আরও ব্যাটারির চার্জ খরচ হতে পারে।\n\nএই অনুমতি বন্ধ করা থাকলে, আগে থেকে থাকা অ্যালার্ম এবং অ্যাপের মাধ্যমে শিডিউল করা সময় ভিত্তিক ইভেন্টের রিমাইন্ডার কাজ করবে না।"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"শিডিউল, অ্যালার্ম, রিমাইন্ডার, ঘড়ি"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"চালু করুন"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'বিরক্ত করবে না\' মোড চালু করুন"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index d3049b13eb93..88abe33d1f18 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -567,6 +567,8 @@ <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index eab1cd2b71a5..f499ab6cc7c0 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Àlies"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 7376d7eeaa93..4d07eff0e858 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -568,6 +568,8 @@ <string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Host"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 1dc2d146622b..c169d768cceb 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæst"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index f092a5198f6c..5808b6ded540 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Wecker und Erinnerungen"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Erlauben, Wecker und Erinnerungen einzurichten"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wecker und Erinnerungen"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dieser App erlauben, Wecker zu stellen und zeitgebundene Aktionen zu planen. Dadurch läuft die App im Hintergrund. Dies kann den Akkuverbrauch erhöhen. \n\nWenn diese Berechtigung deaktiviert ist, funktionieren bereits gestellte Wecker und zeitgebundene Ereignisse, die von dieser App geplant sind, nicht wie erwartet."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planen, Wecker, Erinnerung, Uhr"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivieren"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"„Bitte nicht stören“ aktivieren"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Alias"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 5d4301c2afd3..98f53011d2ca 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Ξυπνητήρια και ειδοποιήσεις"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Να επιτρέπεται ο ορισμός ξυπνητ. και υπενθυμίσεων"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ξυπνητήρια και υπενθυμίσεις"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Επιτρέψτε σε αυτήν την εφαρμογή να ορίζει ξυπνητήρια και να προγραμματίζει ενέργειες που εξαρτώνται από τον χρόνο. Αυτό επιτρέπει στην εφαρμογή να εκτελείται στο παρασκήνιο και, ως εκ τούτου, μπορεί να καταναλώνει περισσότερη μπαταρία.\n\nΑν αυτή η άδεια δεν είναι ενεργή, τα υπάρχοντα ξυπνητήρια και συμβάντα βάσει χρόνου που έχουν προγραμματιστεί από αυτήν την εφαρμογή δεν θα λειτουργούν."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"χρονοδιάγραμμα, ξυπνητήρι, υπενθύμιση, ρολόι"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ενεργοποίηση"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ενεργοποίηση λειτουργίας \"Μην ενοχλείτε\""</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 02317eae12e6..03004a122616 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -566,6 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index f15db49c2af1..42a714d36304 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -566,6 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 02317eae12e6..03004a122616 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -566,6 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 02317eae12e6..03004a122616 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -566,6 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index ce5a6bc780e1..fafc76dc67b2 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -566,6 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index d050ff874df3..5d0a61ab2047 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmas y recordatorios"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permitir configuración de alarmas y recordatorios"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta app establezca alarmas y programe acciones para horarios específicos. De esta manera, la app puede ejecutarse en segundo plano, lo que podría aumentar el consumo de batería.\n\nSi se desactiva este permiso, no funcionarán las alarmas ni los eventos basados en el tiempo existentes que programe esta app."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar No interrumpir"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 540a47200e46..6ff48541e4e8 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Apodo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index f740cc5495a4..ef7b7db2bb9b 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 06e1abd70e78..93506105d964 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Goitizena"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 92e624dcaf71..eb68f5fe2bbb 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string> <string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string> <string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 11bcb3a36b06..964162b34357 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Herätykset ja muistutukset"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Salli herätysten ja muistutusten lisääminen"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Herätykset ja muistutukset"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Anna sovelluksen lisätä herätyksiä ja ajoittaa kiireellisiä tapahtumia. Näin sovellus voi toimia taustalla, mikä voi kuluttaa enemmän virtaa.\n\nIlman tätä lupaa sovelluksen ajoittamat herätykset ja aikaan perustuvat tapahtumat eivät toimi."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajoitus, herätys, muistutus, kello"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ota käyttöön"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ota Älä häiritse ‑tila käyttöön"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index a622385a652a..551dc36faac0 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index c4797fc33b7b..5ade70f74e9e 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 0d009de170ce..73f98ec23ccf 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmas e recordatorios"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permitir axuste de alarmas e recordatorios"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas e recordatorios"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación defina alarmas e planifique accións que dependan da hora. Con este permiso, a aplicación pode executarse en segundo plano, o que pode provocar un maior consumo de batería.\n\nSe este permiso está desactivado, non funcionarán as alarmas que xa se definisen nin os eventos que dependan da hora planificados por esta aplicación."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planificar, alarma, recordatorio, reloxo"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar modo Non molestar"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Alcume"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index b8d8f3597176..b55a5a23493f 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"અલાર્મ અને રિમાઇન્ડર"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"અલાર્મ અને રિમાન્ડરના સેટિંગની મંજૂરી આપો"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"અલાર્મ અને રિમાઇન્ડર"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"આ ઍપને અલાર્મ સેટ કરવા અને સમય પ્રતિ સંવેદનશીલ ક્રિયાઓ શેડ્યૂલ કરવા માટે મંજૂરી આપો. આ ઍપને બૅકગ્રાઉન્ડમાં ચાલવા દે છે, જેને કારણે બૅટરીનો વધુ વપરાશ થઈ શકે છે.\n\nજો આ પરવાનગી બંધ હોય, તો આ ઍપ દ્વારા શેડ્યૂલ કરવામાં આવેલા વર્તમાન અલાર્મ અને સમય આધારિત ઇવેન્ટ કામ કરશે નહીં."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"શેડ્યૂલ, અલાર્મ, રિમાઇન્ડર, ઘડિયાળ"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ચાલુ કરો"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ખલેલ પાડશો નહીં ચાલુ કરો"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"ઉપનામ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 8abbfcf74094..5620d946b999 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string> <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 217ff15ef20d..0ff35f890407 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -567,6 +567,8 @@ <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index e75b0ddf5b17..9bd4d1263abd 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Becenév"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 44b322d0b2da..fcd3c87454e2 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 4bb472417c1a..f67c068c75c0 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index b224330064db..662a67361b33 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Vekjarar og áminningar"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Leyfa stillingu vekjara og áminninga"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Vekjarar og áminningar"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leyfa þessu forriti að stilla vekjara og áætla aðgerðir sem þurfa að eiga sér stað innan ákveðins tímaramma. Þetta leyfir forritinu að keyra í bakgrunninum sem getur notað meiri rafhlöðuorku.\n\nEf slökkt er á þessari heimild munu núverandi vekjarar og tímasettir viðburðir sem þetta forrit stillir ekki virka."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"áætlun, vekjari, áminning, klukka"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Kveikja"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Kveikja á „Ónáðið ekki“"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 1d2295c7a47a..2c2d11fecda4 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 5c894a1bab37..dc4836213508 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -568,6 +568,8 @@ <string name="user_nickname" msgid="262624187455825083">"כינוי"</string> <string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח/ת"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"אורח"</string> <string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 3f23c75eaf44..7e9949f72602 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string> <string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 472377196781..eef252abceeb 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"მეტსახელი"</string> <string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"სტუმარი"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index baae75b17073..ddae3c72babb 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index fdbcba4aa1e7..d970e9590c36 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះហៅក្រៅ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 21dce27988c6..8bad32006594 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"ಅಲಾರಾಮ್ಗಳು ಮತ್ತು ರಿಮೈಂಡರ್ಗಳು"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ಅಲಾರಂಗಳು ಮತ್ತು ರಿಮೈಂಡರ್ಗಳನ್ನು ಹೊಂದಿಸಲು ಅನುಮತಿಸಿ"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ಅಲಾರಂಗಳು ಮತ್ತು ರಿಮೈಂಡರ್ಗಳು"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ಅಲಾರಂಗಳನ್ನು ಹೊಂದಿಸಲು ಮತ್ತು ಸಮಯ-ಸೂಕ್ಷ್ಮವಾದ ಕ್ರಿಯೆಗಳನ್ನು ನಿಗದಿಪಡಿಸಲು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ. ಇದು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಅದರಿಂದ ಹೆಚ್ಚು ಬ್ಯಾಟರಿ ಬಳಕೆಯಾಗಬಹುದು.\n\nಈ ಅನುಮತಿ ಆಫ್ ಆಗಿದ್ದರೆ, ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಅಲಾರಂಗಳು ಮತ್ತು ಈ ಆ್ಯಪ್ ನಿಗದಿಪಡಿಸಿದ ಸಮಯ-ಸೂಕ್ಷ್ಮ ಈವೆಂಟ್ಗಳು ಕೆಲಸ ಮಾಡುವುದಿಲ್ಲ."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ವೇಳಾಪಟ್ಟಿ, ಅಲಾರಂ, ರಿಮೈಂಡರ್, ಗಡಿಯಾರ"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ಆನ್ ಮಾಡಿ"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಅನ್ನು ಆನ್ ಮಾಡಿ"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 6e6a5a76d766..592707190eec 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"닉네임"</string> <string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"게스트"</string> <string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 140eb3bb1a30..618b7db9a988 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Конок"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index f4962f94a09f..bf6c2a32ab7a 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"ໂມງປຸກ ແລະ ການແຈ້ງເຕືອນ"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ອະນຸຍາດໃຫ້ຕັ້ງໂມງປຸກ ແລະ ການແຈ້ງເຕືອນ"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ໂມງປຸກ ແລະ ການແຈ້ງເຕືອນ"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ອະນຸຍາດໃຫ້ແອັບນີ້ຕັ້ງໂມງປຸກ ແລະ ກຳນົດເວລາຄຳສັ່ງທີ່ເນັ້ນເລື່ອງເວລາເປັນສຳຄັນໄດ້. ນີ້ຈະເຮັດໃຫ້ແອັບເຮັດວຽກໄດ້ໃນພື້ນຫຼັງ, ເຊິ່ງອາດໃຊ້ແບັດເຕີຣີຫຼາຍຂຶ້ນ.\n\nຫາກປິດການອະນຸຍາດນີ້ໄວ້, ໂມງປຸກທີ່ມີຢູ່ກ່ອນແລ້ວ ແລະ ເຫດການທີ່ອ້າງອີງເວລາທີ່ກຳນົດໄວ້ໂດຍແອັບນີ້ຈະບໍ່ສາມາດເຮັດວຽກໄດ້."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ກຳນົດເວລາ, ໂມງປຸກ, ການແຈ້ງເຕືອນ, ໂມງ"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ເປີດ"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ເປີດໂໝດຫ້າມລົບກວນ"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ແຂກ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index e920b31ea633..3383602753f1 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -568,6 +568,8 @@ <string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index c43d879a9d2c..d655afc7ae35 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -567,6 +567,8 @@ <string name="user_nickname" msgid="262624187455825083">"Segvārds"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 81f75c570b45..bd7f074f01a0 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Прекар"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додајте гостин"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 9583b506cf14..1e80ccd0f3ab 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string> <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 955937b93992..40b65efe275e 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Хоч"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 8ddb2981e3fc..6a46edee6ce5 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"अलार्म आणि रिमाइंडर"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म आणि रिमाइंडर सेट करण्याची अनुमती द्या"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म आणि रिमाइंडर"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"या ॲपला अलार्म सेट करण्याची किंवा वेळेनुसार संवेदनशील असलेल्या कृती शेड्युल करण्याची अनुमती द्या. हे ॲपला बॅकग्राउंडमध्ये रन होऊ देते, ज्यामुळे जास्त बॅटरी वापरली जाऊ शकते.\n\nही परवानगी बंद असल्यास, सध्याचे अलार्म आणि या ॲपद्वारे शेड्युल केलेले वेळेवर आधारित इव्हेंट काम करणार नाहीत."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्युल, अलार्म, रिमाइंडर, घड्याळ"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सुरू करा"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"व्यत्यय आणू नका सुरू करा"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 13788c43bea8..77006fc1bf1a 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index dac55c5a5e3b..7156a8ef6548 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 769b468df215..bb93a29feaa5 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 0bf1a7802a0c..a54fc0c85071 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"अलार्म र रिमाइन्डरहरू"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म तथा रिमाइन्डर सेट गर्न दिइयोस्"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"घडी तथा रिमाइन्डरहरू"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन तय गर्ने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा यसले चलेर धेरै ब्याट्री खपत गर्न सक्छ।\n\nयो अनुमति दिइएको छैन भने समय तोकिएका अलार्म र यो एपले तय गरेका समयअनुसार चल्ने कार्यक्रमले काम गर्दैन।"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"समयतालिका, अलार्म, रिमाइन्डर, घडी"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सक्रिय गर्नुहोस्"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"बाधा नपुऱ्याउनुहोस् नामक मोडलाई सक्रिय गर्नुहोस्"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"उपनाम"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index ca4308c6c92e..a33bbf3e2dd9 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 21bd1ad4a0e8..6cc3cc560e53 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ଆଲାରାମ ଓ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ ସେଟ କରିବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ଏହି ଆପକୁ ଆଲାରାମ୍ ସେଟ୍ କରିବାକୁ ଏବଂ ସମୟ-ସମ୍ବେଦନଶୀଳ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଏହା ଆପକୁ ପୃଷ୍ଠପଟରେ ଚାଲିବାକୁ ଦେଇଥାଏ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ।\n\nଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଅଛି, ତେବେ ଏହି ଆପ୍ ଦ୍ୱାରା ସିଡୁଲ୍ କରାଯାଇଥିବା ପୂର୍ବରୁ ଥିବା ଆଲାରାମ୍ ଏବଂ ସମୟ-ଆଧାରିତ ଇଭେଣ୍ଟଗୁଡ଼ିକ କାମ କରିବ ନାହିଁ।"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ସିଡୁଲ୍, ଆଲାରାମ୍, ରିମାଇଣ୍ଡର୍, ଘଣ୍ଟା"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ଚାଲୁ କରନ୍ତୁ"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍ କରନ୍ତୁ"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index aaa892a4969b..0af499023717 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ ਸੈੱਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ਇਸ ਐਪ ਨੂੰ ਅਲਾਰਮ ਸੈੱਟ ਕਰਨ ਜਾਂ ਹੋਰ ਸਮਾਂ-ਸੰਵੇਦਨਸ਼ੀਲ ਕਾਰਵਾਈਆਂ ਨੂੰ ਨਿਯਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ। ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚਲਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ, ਜੋ ਵੱਧ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੀ ਹੈ।\n\nਜੇ ਇਹ ਇਜਾਜ਼ਤ ਬੰਦ ਹੈ, ਤਾਂ ਮੌਜੂਦਾ ਅਲਾਰਮ ਅਤੇ ਇਸ ਐਪ ਰਾਹੀਂ ਸਮਾਂ ਨਿਯਤ ਕੀਤੇ ਸਮਾਂ-ਆਧਾਰਿਤ ਇਵੈਂਟਾਂ ਕੰਮ ਨਹੀਂ ਕਰਨਗੇ।"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ਸਮਾਂ-ਸੂਚੀ, ਅਲਾਰਮ, ਰਿਮਾਈਂਡਰ, ਘੜੀ"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ਚਾਲੂ ਕਰੋ"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 99faea7afc10..5f410a86691b 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -568,6 +568,8 @@ <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gość"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 021a75e724bb..3c8d928a7213 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Apelido"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index cdea8b2a4512..fc98aa732606 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Alcunha"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 021a75e724bb..3c8d928a7213 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Apelido"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index e02bada35233..4db2f315a452 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -567,6 +567,8 @@ <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 923917b0eeba..0bcdc230c089 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -568,6 +568,8 @@ <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 3c79bf38ccd8..a385055f8fbb 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string> <string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"අමුත්තා"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 5be0d993e7ce..ef12ad36c5e1 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -568,6 +568,8 @@ <string name="user_nickname" msgid="262624187455825083">"Prezývka"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 07e367bab045..cbdfefcd424a 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -568,6 +568,8 @@ <string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 1f30a02eb155..ba7443600e3d 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmet dhe alarmet rikujtuese"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Lejo caktimin e alarmeve dhe alarmeve rikujtuese"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmet dhe alarmet rikujtuese"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lejo që ky aplikacion të caktojë alarmet dhe të planifikojë veprime që kanë një afat të caktuar. Kjo lejon që aplikacioni të ekzekutohet në sfond, gjë që mund të përdorë më shumë bateri.\n\nNëse kjo leje është caktuar si joaktive, alarmet ekzistuese dhe ngjarjet me bazë kohore të planifikuara nga ky apliikacion nuk do të funksionojnë."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planifiko, alarm, alarm rikujtues, ora"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivizo"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivizo \"Mos shqetëso\""</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 11adbe529ff4..3859be937d47 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -567,6 +567,8 @@ <string name="user_nickname" msgid="262624187455825083">"Надимак"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index f4b92afa216e..7dcca0d2b611 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 2625a414ebbf..332cd9dc386a 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ongeza mgeni"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 612fd700031e..7952a76859f5 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -567,6 +567,8 @@ <string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string> <string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"கெஸ்ட்"</string> <string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 7f596e301b05..da0380d13107 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"అలారాలు, రిమైండర్లు"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"అలారాలు, రిమైండర్లను సెట్ చేయడానికి అనుమతించండి"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"అలారాలు & రిమైండర్లు"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"అలారాలను సెట్ చేయడానికి, సమయ-సునిశిత చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. ఇది యాప్ను బ్యాక్గ్రౌండ్లో రన్ అవడానికి అనుమతిస్తుంది, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు.\n\nఈ అనుమతిని ఆఫ్ చేస్తే, ఈ యాప్ ద్వారా షెడ్యూల్ చేసిన ఇప్పటికే ఉన్న అలారాలు, సమయ-ఆధారిత ఈవెంట్లు పనిచేయవు."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"షెడ్యూల్, అలారం, రిమైండర్, గడియారం"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ఆన్ చేయండి"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"అంతరాయం కలిగించవద్దును ఆన్ చేయండి"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string> <string name="guest_new_guest" msgid="3482026122932643557">"గెస్ట్ను జోడించండి"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"గెస్ట్ను తీసివేయండి"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"గెస్ట్"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్ను ఎంచుకోండి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 3851ccb0c8a2..b7aff721c405 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string> <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้ใช้ชั่วคราว"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้ใช้ชั่วคราวออก"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index a381c351c98c..6d1f6abbdf96 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 03efe82b0c93..36144ebf1c63 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Takma ad"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index e737c7dc7089..82e481bd0613 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -568,6 +568,8 @@ <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Гість"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 45e9f4f91a4a..d0ed2ab8ba34 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -507,8 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"الارمز اور یاد دہانیاں"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"الارمز اور یاد دہانیاں سیٹ کرنے کی اجازت دیں"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"الارمز اور یاد دہانیاں"</string> - <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) --> - <skip /> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"اس ایپ کو الارمز سیٹ کرنے اور متعین وقت کے لحاظ سے حساس کارروائیوں کو شیڈول کرنے کی اجازت دیں۔ یہ ایپ کو پس منظر میں چلنے دیتا ہے، جس میں زیادہ بیٹری استعمال ہو سکتی ہے۔\n\n اگر یہ اجازت آف ہے تو موجودہ الارمز اور اس ایپ کے ذریعے شیڈول کردہ وقت پر مبنی ایونٹس کام نہیں کریں گے۔"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"شیڈول، الارم، یاد دہانی، گھڑی"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"آن کریں"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ڈسٹرب نہ کریں\' کو آن کریں"</string> @@ -567,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string> <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 96b8853dce8f..7f4e30aa33c4 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Nik"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmonni olib tashlash"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 4509430f00e9..0fba1ecffe02 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Khách"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 3e6b3c2b4afe..d226f0170ac6 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -507,7 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"闹钟和提醒"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"允许设置闹钟和提醒"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"闹钟和提醒"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允许该应用设置闹钟以及安排在特定时间执行某些操作。如此一来,该应用将在后台运行,而这可能会消耗更多电池电量。\n\n如果您关闭了此权限,该应用设置的现有闹钟将不会响起,而且该应用安排在特定时间执行的现有活动也不会执行。"</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允许该应用设置闹钟以及安排在特定时间执行某些操作。这项权限开启后,该应用将在后台运行,可能会消耗更多电池电量。\n\n如果您关闭此权限,该应用设置的现有闹钟将不会响起,而且该应用安排在特定时间执行的现有活动也不会执行。"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"设置, 闹钟, 提醒, 时钟, schedule, alarm, reminder, clock"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"开启"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"开启勿扰模式"</string> @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"昵称"</string> <string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"访客"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 42d95b7ffeae..642dd1dcb4ed 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 20fa1876ef24..1cab0afbead2 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index f49d3cc9ba2d..2fe791a5a47c 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -566,6 +566,8 @@ <string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string> + <!-- no translation found for guest_reset_guest (6110013010356013758) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 603d09344e2e..8651595753a7 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1422,6 +1422,8 @@ <string name="guest_new_guest">Add guest</string> <!-- Label for exiting and removing the guest session in the user switcher [CHAR LIMIT=35] --> <string name="guest_exit_guest">Remove guest</string> + <!-- Label for resetting guest session in the user switcher, which will remove all data from the current guest session [CHAR LIMIT=35] --> + <string name="guest_reset_guest">Reset guest</string> <!-- Name for the guest user [CHAR LIMIT=35] --> <string name="guest_nickname">Guest</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java index a5373944474c..0b3a519cd919 100644 --- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java +++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java @@ -16,8 +16,10 @@ package com.android.settingslib.enterprise; +import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.Context; +import android.content.DialogInterface; import androidx.annotation.Nullable; @@ -54,4 +56,13 @@ public interface ActionDisabledByAdminController { * Updates the enforced admin */ void updateEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin, @UserIdInt int adminUserId); + + /** + * Returns a listener for handling positive button clicks + */ + @Nullable + default DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context, + @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) { + return null; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java index da42e330b8b4..44cafb17e1d8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java +++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java @@ -20,6 +20,11 @@ import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.ParentalControlsUtilsInternal; +import android.os.UserHandle; +import android.os.UserManager; +import android.text.TextUtils; /** * A factory that returns the relevant instance of {@link ActionDisabledByAdminController}. @@ -30,10 +35,28 @@ public final class ActionDisabledByAdminControllerFactory { * Returns the relevant instance of {@link ActionDisabledByAdminController}. */ public static ActionDisabledByAdminController createInstance(Context context, - DeviceAdminStringProvider stringProvider) { - return isFinancedDevice(context) - ? new FinancedDeviceActionDisabledByAdminController(stringProvider) - : new ManagedDeviceActionDisabledByAdminController(stringProvider); + String restriction, DeviceAdminStringProvider stringProvider) { + if (doesBiometricRequireParentalConsent(context, restriction)) { + return new BiometricActionDisabledByAdminController(stringProvider); + } else if (isFinancedDevice(context)) { + return new FinancedDeviceActionDisabledByAdminController(stringProvider); + } else { + return new ManagedDeviceActionDisabledByAdminController(stringProvider); + } + } + + /** + * @return true if the restriction == UserManager.DISALLOW_BIOMETRIC and parental consent + * is required. + */ + private static boolean doesBiometricRequireParentalConsent(Context context, + String restriction) { + if (!TextUtils.equals(UserManager.DISALLOW_BIOMETRIC, restriction)) { + return false; + } + DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); + return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm, + BiometricAuthenticator.TYPE_ANY_BIOMETRIC, new UserHandle(UserHandle.myUserId())); } private static boolean isFinancedDevice(Context context) { diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java new file mode 100644 index 000000000000..814d5d23f458 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.enterprise; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.util.Log; + +import androidx.annotation.Nullable; + +import com.android.settingslib.RestrictedLockUtils; + +public class BiometricActionDisabledByAdminController extends BaseActionDisabledByAdminController { + + private static final String TAG = "BiometricActionDisabledByAdminController"; + + // These MUST not change, as they are the stable API between here and device admin specified + // by the component below. + private static final String ACTION_LEARN_MORE = "android.settings.LEARN_MORE"; + private static final String EXTRA_FROM_BIOMETRIC_SETUP = "from_biometric_setup"; + + BiometricActionDisabledByAdminController( + DeviceAdminStringProvider stringProvider) { + super(stringProvider); + } + + @Override + public void setupLearnMoreButton(Context context) { + + } + + @Override + public String getAdminSupportTitle(@Nullable String restriction) { + return mStringProvider.getDisabledBiometricsParentConsentTitle(); + } + + @Override + public CharSequence getAdminSupportContentString(Context context, + @Nullable CharSequence supportMessage) { + return mStringProvider.getDisabledBiometricsParentConsentContent(); + } + + @Override + public DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context, + @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) { + return (dialog, which) -> { + Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component); + final Intent intent = new Intent(ACTION_LEARN_MORE) + .setComponent(enforcedAdmin.component) + .putExtra(EXTRA_FROM_BIOMETRIC_SETUP, true) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + }; + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java index c47d789a514d..b83837e6caf6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java @@ -72,4 +72,14 @@ public interface DeviceAdminStringProvider { * a financed device. */ String getDisabledByPolicyTitleForFinancedDevice(); + + /** + * Returns the dialog title for when biometrics require parental consent. + */ + String getDisabledBiometricsParentConsentTitle(); + + /** + * Returns the dialog contents for when biometrics require parental consent. + */ + String getDisabledBiometricsParentConsentContent(); } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java index b0c5314a2ec0..53a382a9ebf6 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java @@ -89,24 +89,4 @@ public class SettingsSpinnerPreferenceTest { assertThat(mSpinnerPreference.getSelectedItem()) .isEqualTo(mSpinner.getAdapter().getItem(1)); } - - @Test - public void onBindViewHolder_setClickableTrue_isClickableTrue() { - mSpinnerPreference.setClickable(true); - - mSpinnerPreference.onBindViewHolder(mViewHolder); - - assertThat(mSpinner.isClickable()).isTrue(); - assertThat(mSpinner.isEnabled()).isTrue(); - } - - @Test - public void onBindViewHolder_setClickableFalse_isClickableFalse() { - mSpinnerPreference.setClickable(false); - - mSpinnerPreference.onBindViewHolder(mViewHolder); - - assertThat(mSpinner.isClickable()).isFalse(); - assertThat(mSpinner.isEnabled()).isFalse(); - } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java new file mode 100644 index 000000000000..766c2f5f9872 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.enterprise; + +import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN; +import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID; +import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DEVICE_ADMIN_STRING_PROVIDER; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.UserHandle; + +import com.android.settingslib.RestrictedLockUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class BiometricActionDisabledByAdminControllerTest { + + private final ActionDisabledByAdminControllerTestUtils mTestUtils = + new ActionDisabledByAdminControllerTestUtils(); + private final BiometricActionDisabledByAdminController mController = + new BiometricActionDisabledByAdminController(DEFAULT_DEVICE_ADMIN_STRING_PROVIDER); + + @Before + public void setUp() { + mController.initialize(mTestUtils.createLearnMoreButtonLauncher()); + mController.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID); + } + + @Test + public void buttonClicked() { + Context context = mock(Context.class); + ComponentName componentName = mock(ComponentName.class); + RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils.EnforcedAdmin( + componentName, new UserHandle(UserHandle.myUserId())); + + DialogInterface.OnClickListener listener = + mController.getPositiveButtonListener(context, enforcedAdmin); + assertNotNull("Biometric Controller must supply a non-null listener", listener); + listener.onClick(mock(DialogInterface.class), 0 /* which */); + + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(context).startActivity(intentCaptor.capture()); + assertEquals("android.settings.LEARN_MORE", + intentCaptor.getValue().getAction()); + assertTrue("from_biometric_setup", intentCaptor.getValue() + .getBooleanExtra("from_biometric_setup", false)); + assertEquals(componentName, intentCaptor.getValue().getComponent()); + } + +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java index be3e9fcf45ea..99e13c325472 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java @@ -30,6 +30,8 @@ class FakeDeviceAdminStringProvider implements DeviceAdminStringProvider { static final String DEFAULT_DISABLED_BY_POLICY_CONTENT = "default_disabled_by_policy_content"; static final String DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE = "default_disabled_by_policy_title_financed_device"; + static final String DEFAULT_BIOMETRIC_TITLE = "biometric_title"; + static final String DEFAULT_BIOMETRIC_CONTENTS = "biometric_contents"; static final DeviceAdminStringProvider DEFAULT_DEVICE_ADMIN_STRING_PROVIDER = new FakeDeviceAdminStringProvider(/* url = */ null); @@ -88,4 +90,15 @@ class FakeDeviceAdminStringProvider implements DeviceAdminStringProvider { public String getDisabledByPolicyTitleForFinancedDevice() { return DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE; } + + @Override + public String getDisabledBiometricsParentConsentTitle() { + return DEFAULT_BIOMETRIC_TITLE; + } + + @Override + public String getDisabledBiometricsParentConsentContent() { + return DEFAULT_BIOMETRIC_CONTENTS; + } + } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java index 6670ed307816..0a48f19a3021 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java @@ -20,7 +20,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.robolectric.Robolectric.setupActivity; +import static org.robolectric.Shadows.shadowOf; +import android.app.Activity; import android.content.Context; import android.graphics.ColorFilter; import android.graphics.PorterDuff; @@ -44,8 +47,8 @@ import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.Shadows; import org.robolectric.shadows.ShadowDrawable; +import org.robolectric.shadows.ShadowTouchDelegate; import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) @@ -58,6 +61,9 @@ public class BannerMessagePreferenceTest { private boolean mClickListenerCalled = false; private final View.OnClickListener mClickListener = v -> mClickListenerCalled = true; + private final int mMinimumTargetSize = + RuntimeEnvironment.application.getResources() + .getDimensionPixelSize(R.dimen.settingslib_preferred_minimum_touch_target); private static final int TEST_STRING_RES_ID = R.string.accessibility_banner_message_dismiss; @@ -81,6 +87,23 @@ public class BannerMessagePreferenceTest { } @Test + public void onBindViewHolder_andOnLayoutView_dismissButtonTouchDelegate_isCorrectSize() { + assumeAndroidS(); + mBannerPreference.setTitle("Title"); + mBannerPreference.setDismissButtonOnClickListener(mClickListener); + + mBannerPreference.onBindViewHolder(mHolder); + setupActivity(Activity.class).setContentView(mRootView); + + assertThat(mRootView.getTouchDelegate()).isNotNull(); + ShadowTouchDelegate delegate = shadowOf(mRootView.getTouchDelegate()); + assertThat(delegate.getBounds().width()).isAtLeast(mMinimumTargetSize); + assertThat(delegate.getBounds().height()).isAtLeast(mMinimumTargetSize); + assertThat(delegate.getDelegateView()) + .isEqualTo(mRootView.findViewById(R.id.banner_dismiss_btn)); + } + + @Test public void onBindViewHolder_whenSummarySet_shouldSetSummary() { mBannerPreference.setSummary("test"); @@ -157,7 +180,7 @@ public class BannerMessagePreferenceTest { mBannerPreference.onBindViewHolder(mHolder); ImageView mIcon = mRootView.findViewById(R.id.banner_icon); - ShadowDrawable shadowDrawable = Shadows.shadowOf(mIcon.getDrawable()); + ShadowDrawable shadowDrawable = shadowOf(mIcon.getDrawable()); assertThat(shadowDrawable.getCreatedFromResId()) .isEqualTo(R.drawable.settingslib_ic_cross); } @@ -168,7 +191,7 @@ public class BannerMessagePreferenceTest { mBannerPreference.onBindViewHolder(mHolder); ImageView mIcon = mRootView.findViewById(R.id.banner_icon); - ShadowDrawable shadowDrawable = Shadows.shadowOf(mIcon.getDrawable()); + ShadowDrawable shadowDrawable = shadowOf(mIcon.getDrawable()); assertThat(shadowDrawable.getCreatedFromResId()).isEqualTo(R.drawable.ic_warning); } @@ -478,11 +501,15 @@ public class BannerMessagePreferenceTest { private void assumeAndroidR() { ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 30); + ReflectionHelpers.setStaticField(Build.VERSION.class, "CODENAME", "R"); + ReflectionHelpers.setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", false); // Reset view holder to use correct layout. } private void assumeAndroidS() { ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 31); + ReflectionHelpers.setStaticField(Build.VERSION.class, "CODENAME", "S"); + ReflectionHelpers.setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", true); // Re-inflate view to update layout. setUpViewHolder(); } diff --git a/packages/Shell/res/values-az/strings.xml b/packages/Shell/res/values-az/strings.xml index 15853c2cc270..23a1ad7abd7c 100644 --- a/packages/Shell/res/values-az/strings.xml +++ b/packages/Shell/res/values-az/strings.xml @@ -35,7 +35,7 @@ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Zip faylı üçün baq hesabat detalları əlavə edilmədi"</string> <string name="bugreport_unnamed" msgid="2800582406842092709">"adsız"</string> <string name="bugreport_info_action" msgid="2158204228510576227">"Detallar"</string> - <string name="bugreport_screenshot_action" msgid="8677781721940614995">"displey görüntüsü"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Skrinşot"</string> <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Displey görüntüsü uğurla çəkildi."</string> <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Displey görüntüsü əlçatan deyil."</string> <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Baq hesabatı <xliff:g id="ID">#%d</xliff:g> detalları"</string> diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml index b975521c2acb..816fe3b432bc 100644 --- a/packages/Shell/res/values-iw/strings.xml +++ b/packages/Shell/res/values-iw/strings.xml @@ -29,7 +29,7 @@ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"יש להקיש כדי לשתף את הדוח על הבאג ללא צילום מסך, או להמתין להשלמת צילום המסך"</string> <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"יש להקיש כדי לשתף את הדוח על הבאג ללא צילום מסך, או להמתין להשלמת צילום המסך"</string> <string name="bugreport_confirm" msgid="5917407234515812495">"דוחות על באגים כוללים נתונים מקובצי היומן השונים במערכת, שעשויים לכלול נתונים הנחשבים רגישים (כגון שימוש באפליקציות ונתוני מיקום). כדאי לשתף דוחות על באגים רק עם אפליקציות ואנשים מהימנים."</string> - <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"אל תציגו זאת שוב"</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> diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 8bc3d228f7a8..2579e7084e08 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -238,7 +238,9 @@ class ActivityLaunchAnimator( * during the animation. */ @JvmStatic - fun fromView(view: View): Controller = GhostedViewLaunchAnimatorController(view) + fun fromView(view: View, cujType: Int? = null): Controller { + return GhostedViewLaunchAnimatorController(view, cujType) + } } /** diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt index 4b655a1a1b02..ffb7ab4eff7c 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt @@ -14,6 +14,7 @@ import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay import android.widget.FrameLayout +import com.android.internal.jank.InteractionJankMonitor import kotlin.math.min /** @@ -29,7 +30,10 @@ import kotlin.math.min */ open class GhostedViewLaunchAnimatorController( /** The view that will be ghosted and from which the background will be extracted. */ - private val ghostedView: View + private val ghostedView: View, + + /** The [InteractionJankMonitor.CujType] associated to this animation. */ + private val cujType: Int? = null ) : ActivityLaunchAnimator.Controller { /** The container to which we will add the ghost view and expanding background. */ override var launchContainer = ghostedView.rootView as ViewGroup @@ -125,6 +129,8 @@ open class GhostedViewLaunchAnimatorController( val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX matrix.getValues(initialGhostViewMatrixValues) + + cujType?.let { InteractionJankMonitor.getInstance().begin(ghostedView, it) } } override fun onLaunchAnimationProgress( @@ -167,6 +173,8 @@ open class GhostedViewLaunchAnimatorController( } override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { + cujType?.let { InteractionJankMonitor.getInstance().end(it) } + backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha GhostView.removeGhost(ghostedView) diff --git a/packages/SystemUI/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md index 89c28a074c76..efcb2de0f6c3 100644 --- a/packages/SystemUI/docs/qs-tiles.md +++ b/packages/SystemUI/docs/qs-tiles.md @@ -306,6 +306,7 @@ This section describes necessary and recommended steps when implementing a Quick * Add a case to the `switch` with a unique String spec for the chosen tile. 5. In [SystemUI/res/values/config.xml](/packages/SystemUI/res/values/config.xml), modify `quick_settings_tiles_stock` and add the spec defined in the previous step. If necessary, add it also to `quick_settings_tiles_default`. The first one contains a list of all the tiles that SystemUI knows how to create (to show to the user in the customization screen). The second one contains only the default tiles that the user will experience on a fresh boot or after they reset their tiles. 6. In [SystemUI/res/values/tiles_states_strings.xml](/packages/SystemUI/res/values/tiles_states_strings.xml), add a new array for your tile. The name has to be `tile_states_<spec>`. Use a good description to help the translators. +7. In [`SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt), add a new element to the map in `SubtitleArrayMapping` corresponding to the resource created in the previous step. #### Abstract methods in QSTileImpl diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index 298b7c38066b..7c81325d685f 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -92,5 +92,12 @@ public interface ActivityStarter { * *after* returning to start hiding the keyguard. */ boolean onDismiss(); + + /** + * Whether running this action when we are locked will start an animation on the keyguard. + */ + default boolean willRunAnimationOnKeyguard() { + return false; + } } } diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml index 1c99e53690f0..85501640672b 100644 --- a/packages/SystemUI/res-keyguard/values-eu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml @@ -22,16 +22,16 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name" msgid="514691256816366517">"Teklatu-babeslea"</string> <string name="keyguard_password_enter_pin_code" msgid="8582296866585566671">"Idatzi PIN kodea"</string> - <string name="keyguard_password_enter_puk_code" msgid="3813154965969758868">"Idatzi SIM txartelaren PUKa eta PIN berria"</string> + <string name="keyguard_password_enter_puk_code" msgid="3813154965969758868">"Idatzi SIMaren PUKa eta PIN kode berria"</string> <string name="keyguard_password_enter_puk_prompt" msgid="3529260761374385243">"SIM txartelaren PUK kodea"</string> - <string name="keyguard_password_enter_pin_prompt" msgid="2304037870481240781">"SIM txartelaren PIN berria"</string> + <string name="keyguard_password_enter_pin_prompt" msgid="2304037870481240781">"SIMaren PIN kode berria"</string> <string name="keyguard_password_entry_touch_hint" msgid="6180028658339706333"><font size="17">"Pasahitza idazteko, sakatu hau"</font></string> <string name="keyguard_password_enter_password_code" msgid="7393393239623946777">"Idatzi desblokeatzeko pasahitza"</string> <string name="keyguard_password_enter_pin_password_code" msgid="3692259677395250509">"Idatzi desblokeatzeko PINa"</string> <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Idatzi PINa"</string> <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Marraztu eredua"</string> <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Idatzi pasahitza"</string> - <string name="keyguard_password_wrong_pin_code" msgid="3514267777289393046">"PINa ez da zuzena."</string> + <string name="keyguard_password_wrong_pin_code" msgid="3514267777289393046">"PIN kodea okerra da."</string> <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Txartelak ez du balio."</string> <string name="keyguard_charged" msgid="5478247181205188995">"Kargatuta"</string> <string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hari gabe kargatzen"</string> @@ -128,8 +128,8 @@ <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ez da ezagutu"</string> <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ez da ezagutu"</string> <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818"> - <item quantity="other">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item> - <item quantity="one">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item> + <item quantity="other">Idatzi SIMaren PINa. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item> + <item quantity="one">Idatzi SIMaren PINa. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item> </plurals> <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935"> <item quantity="other">Desgaitu egin da SIM txartela. Aurrera egiteko, idatzi PUK kodea. <xliff:g id="_NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu SIM txartela betiko erabilgaitz geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.</item> diff --git a/packages/SystemUI/res/drawable/rounded_bg_full.xml b/packages/SystemUI/res/drawable/rounded_bg_full.xml index e0d3f63e8f40..967c57b0c370 100644 --- a/packages/SystemUI/res/drawable/rounded_bg_full.xml +++ b/packages/SystemUI/res/drawable/rounded_bg_full.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> - <solid android:color="?android:attr/colorBackgroundFloating" /> + <solid android:color="?androidprv:attr/colorSurface" /> <corners android:bottomLeftRadius="?android:attr/dialogCornerRadius" android:topLeftRadius="?android:attr/dialogCornerRadius" diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/SystemUI/res/layout/global_actions_change_panel.xml index dffb0f011bb5..bc9c203b299a 100644 --- a/packages/SystemUI/res/layout/global_actions_change_panel.xml +++ b/packages/SystemUI/res/layout/global_actions_change_panel.xml @@ -14,8 +14,18 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<ImageView +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/global_actions_change_button" android:layout_width="wrap_content" - android:layout_height="wrap_content"/> + android:layout_height="wrap_content"> + <TextView + android:id="@+id/global_actions_change_message" + android:layout_width="wrap_content" + android:visibility="gone" + android:layout_height="wrap_content" + android:text="@string/global_actions_change_description" /> + <ImageView + android:id="@+id/global_actions_change_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml index 4765b44d78c8..3f4baaf27b84 100644 --- a/packages/SystemUI/res/layout/long_screenshot.xml +++ b/packages/SystemUI/res/layout/long_screenshot.xml @@ -99,6 +99,7 @@ app:handleThickness="@dimen/screenshot_crop_handle_thickness" app:handleColor="?androidprv:attr/colorAccentPrimary" app:scrimColor="@color/screenshot_crop_scrim" + app:containerBackgroundColor="?android:colorBackgroundFloating" tools:background="?android:colorBackground" tools:minHeight="100dp" tools:minWidth="100dp" /> diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml index 42f14f30da00..6bc01389e3c3 100644 --- a/packages/SystemUI/res/layout/notif_half_shelf.xml +++ b/packages/SystemUI/res/layout/notif_half_shelf.xml @@ -64,10 +64,12 @@ android:layout_gravity="center" android:padding="8dp" android:gravity="center_vertical|start" - android:textSize="15sp" android:ellipsize="end" - android:maxLines="1" - style="@style/TextAppearance.NotificationImportanceChannel" /> + android:maxLines="2" + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textSize="16sp" + /> <Switch android:id="@+id/toggle" @@ -80,18 +82,13 @@ <View android:layout_width="match_parent" android:layout_height="1dp" - android:background="?android:attr/colorAccent" + android:background="@*android:color/background_device_default_light" /> <!-- ChannelRows get added dynamically --> </com.android.systemui.statusbar.notification.row.ChannelEditorListView> - <!-- divider view --> - <View - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?android:attr/colorAccent" - /> + <RelativeLayout android:id="@+id/bottom_actions" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml index 87de28650662..245d1579d5a3 100644 --- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml +++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml @@ -21,61 +21,76 @@ android:layout_height="wrap_content" android:padding="8dp" android:clickable="true" - android:orientation="horizontal" - android:foreground="?android:attr/selectableItemBackground" > - - <!-- This is where an icon would go *if we wanted one* **wink** --> - <Space - android:id="@+id/icon" - android:layout_height="48dp" - android:layout_width="48dp" - android:layout_gravity="center_vertical" - android:padding="8dp" - /> + android:orientation="vertical" + android:foreground="?android:attr/selectableItemBackground" + > - <RelativeLayout - android:id="@+id/description_container" + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_width="0dp" - android:layout_weight="1" - android:layout_gravity="center_vertical" - android:gravity="left|center_vertical" - android:orientation="vertical" - > - <TextView - android:id="@+id/channel_name" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:paddingBottom="0dp" - android:paddingStart="8dp" - android:paddingEnd="8dp" - android:gravity="center_vertical|start" - android:textSize="14sp" - android:ellipsize="end" - android:maxLines="1" - style="@style/TextAppearance.NotificationImportanceChannel" + android:orientation="horizontal"> + + <!-- This is where an icon would go *if we wanted one* **wink** --> + <Space + android:id="@+id/icon" + android:layout_height="48dp" + android:layout_width="48dp" + android:layout_gravity="center_vertical" + android:padding="8dp" /> - <TextView - android:id="@+id/channel_description" + <RelativeLayout + android:id="@+id/description_container" android:layout_height="wrap_content" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_gravity="center_vertical" + android:gravity="left|center_vertical" + android:orientation="vertical" + > + <TextView + android:id="@+id/channel_name" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:paddingBottom="0dp" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:gravity="center_vertical|start" + android:ellipsize="end" + android:maxLines="1" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:textColor="?android:attr/textColorPrimary" + android:textSize="16sp" + /> + + <TextView + android:id="@+id/channel_description" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:gravity="center_vertical|start" + android:ellipsize="end" + android:maxLines="1" + android:layout_below="@id/channel_name" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textColor="?android:attr/textColorSecondary" + android:textSize="14sp" + /> + </RelativeLayout> + + <Switch + android:id="@+id/toggle" + android:layout_height="48dp" android:layout_width="wrap_content" - android:paddingStart="8dp" - android:paddingEnd="8dp" - android:gravity="center_vertical|start" - android:textSize="14sp" - android:ellipsize="end" - android:maxLines="1" - android:layout_below="@id/channel_name" - style="@style/TextAppearance.NotificationImportanceApp" + android:layout_gravity="center_vertical" + android:padding="8dp" /> - </RelativeLayout> - - <Switch - android:id="@+id/toggle" - android:layout_height="48dp" - android:layout_width="wrap_content" - android:layout_gravity="center_vertical" - android:padding="8dp" + </LinearLayout> + <!-- divider view --> + <View + android:layout_width="match_parent" + android:layout_height=".5dp" + android:background="@*android:color/background_device_default_light" /> </com.android.systemui.statusbar.notification.row.ChannelRow> diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml index 1d93f5d69da8..536b0423ce16 100644 --- a/packages/SystemUI/res/layout/qs_tile_label.xml +++ b/packages/SystemUI/res/layout/qs_tile_label.xml @@ -33,6 +33,7 @@ android:gravity="start" android:textDirection="locale" android:ellipsize="marquee" + android:marqueeRepeatLimit="1" android:singleLine="true" android:textAppearance="@style/TextAppearance.QS.TileLabel"/> @@ -43,6 +44,7 @@ android:gravity="start" android:textDirection="locale" android:ellipsize="marquee" + android:marqueeRepeatLimit="1" android:singleLine="true" android:visibility="gone" android:textAppearance="@style/TextAppearance.QS.TileLabel.Secondary" diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index c88703dabeea..5b9ca1b26158 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -59,11 +59,16 @@ android:visibility="gone" /> - <LinearLayout + <FrameLayout android:id="@+id/rightLayout" android:layout_width="wrap_content" android:layout_height="match_parent" - android:gravity="center_vertical|end" + android:gravity="end" + > + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="center_vertical|end" > <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons" @@ -80,4 +85,6 @@ android:paddingEnd="2dp" /> </LinearLayout> + </FrameLayout> + </LinearLayout> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 214937105315..f8d06ce76c24 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Voeg gebruiker by"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuwe gebruiker"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Verwyder gas?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Stel gassessie terug?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwyder"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Stel terug"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gas!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Vergroot \'n deel van die skerm"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Wissel"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Toeganklikheidknoppie het die toeganklikheidgebaar vervang\n\n"<annotation id="link">"Bekyk instellings"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Jy kan van die toeganklikheidsgebaar na \'n -knoppie oorskakel\n\n"<annotation id="link">"Instellings"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Skuif knoppie na kant om dit tydelik te versteek"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Beweeg na links bo"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Beweeg na regs bo"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index fe3cc8eaeb06..4736adbfb62f 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"ተጠቃሚ አክል"</string> <string name="user_new_user_name" msgid="2019166282704195789">"አዲስ ተጠቃሚ"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"እንግዳ ይወገድ?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"አስወግድ"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"እንኳን በደህና ተመለሱ እንግዳ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"የማያ ገጹን ክፍል አጉላ"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ማብሪያ/ማጥፊያ"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"የተደራሽነት አዝራር የተደራሽነት ምልክትን ተክቷል\n\n"<annotation id="link">" ቅንብሮችን አሳይ"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ከተደራሽነት ምልክቱ ወደ አንድ አዝራር መቀየር ይችላሉ።\n\n"<annotation id="link">"ከተደ"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ለጊዜው ለመደበቅ አዝራሩን ወደ ጠርዝ ያንቀሳቅሱ"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ወደ ላይኛው ግራ አንቀሳቅስ"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 83b483e81463..b493d897fd22 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -488,8 +488,12 @@ <string name="user_add_user" msgid="4336657383006913022">"إضافة مستخدم"</string> <string name="user_new_user_name" msgid="2019166282704195789">"مستخدم جديد"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"هل تريد إزالة جلسة الضيف؟"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"إزالة"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مرحبًا بك مجددًا في جلسة الضيف"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string> @@ -1054,6 +1058,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"تكبير جزء من الشاشة"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"تبديل"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"تم استبدال \"زر أدوات تسهيل الاستخدام\" بإيماءة تسهيل الاستخدام.\n\n"<annotation id="link">"الاطّلاع على الإعدادات"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"يمكنك التبديل من إيماءة تسهيل الاستخدام إلى استخدام زرّ\n\n"<annotation id="link">"الإعدادات"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"يمكنك نقل الزر إلى الحافة لإخفائه مؤقتًا."</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"نقل إلى أعلى يمين الشاشة"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"نقل إلى أعلى يسار الشاشة"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 50d866d0ce54..fa528fd9037a 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ব্যৱহাৰকাৰী যোগ কৰক"</string> <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যৱহাৰকাৰী"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি আঁতৰাবনে?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"অতিথিৰ ছেশ্বন ৰিছেট কৰিবনে?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ সকলো এপ্ আৰু ডেটা মচা হ\'ব।"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"আঁতৰাওক"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ৰিছেট কৰক"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"অতিথি, আপোনাক পুনৰ স্বাগতম!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আকৌ আৰম্ভ কৰক"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্ৰীনৰ কিছু অংশ বিবৰ্ধন কৰক"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ছুইচ"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"সাধ্য সুবিধাৰ বুটামটোৱে সাধ্য সুবিধাৰ নিৰ্দেশ সলনি কৰিছে\n\n"<annotation id="link">"ছেটিং চাওক"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"আপুনি সাধ্য-সুবিধাৰ নিৰ্দেশটোৰ পৰা এটা বুটামলৈ সলনি কৰিব পাৰে\n\n"<annotation id="link">"ছেটিং"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"বুটামটোক সাময়িকভাৱে লুকুৱাবলৈ ইয়াক একেবাৰে কাষলৈ লৈ যাওক"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"শীৰ্ষৰ বাওঁফালে নিয়ক"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"শীৰ্ষৰ সোঁফালে নিয়ক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 9784d534a163..9502454d1c8c 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -89,7 +89,7 @@ <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Skrinşot çəkməyə tətbiq və ya təşkilat tərəfindən icazə verilmir"</string> <string name="screenshot_edit_label" msgid="8754981973544133050">"Redaktə edin"</string> <string name="screenshot_edit_description" msgid="3333092254706788906">"Skrinşota düzəliş edin"</string> - <string name="screenshot_scroll_label" msgid="2930198809899329367">"Daha çoxunu əhatə edin"</string> + <string name="screenshot_scroll_label" msgid="2930198809899329367">"Genişləndirin"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ekran şəklini ötürün"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string> <string name="screenshot_top_boundary_pct" msgid="2520148599096479332">"Yuxarı sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string> @@ -100,7 +100,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilişi emal edilir"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Yazmağa başlanılsın?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"Yazarkən Android Sistemi ekranınızda görünən və ya cihazınızda göstərilən istənilən həssas məlumatı qeydə ala bilər. Buraya parollar, ödəniş məlumatı, fotolar, mesajlar və audio daxildir."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"Ekranda görünən və ya cihazda oxudulan şəxsi məlumat (parol, bank hesabı, mesaj, fotoşəkil və sair) videoyazıya düşə bilər."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Audio yazın"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Cihaz audiosu"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Cihazınızdan gələn musiqi, zənglər və zəng melodiyaları kimi səslər"</string> @@ -109,7 +109,7 @@ <string name="screenrecord_start" msgid="330991441575775004">"Başlayın"</string> <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Ekran yazılır"</string> <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Ekran və audio yazılır"</string> - <string name="screenrecord_taps_label" msgid="1595690528298857649">"Ekranda toxunuşları göstərin"</string> + <string name="screenrecord_taps_label" msgid="1595690528298857649">"Ekrana toxunuş göstərilsin"</string> <string name="screenrecord_stop_text" msgid="6549288689506057686">"Dayandırmaq üçün toxunun"</string> <string name="screenrecord_stop_label" msgid="72699670052087989">"Dayandırın"</string> <string name="screenrecord_pause_label" msgid="6004054907104549857">"Dayandırın"</string> @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"İstifadəçi əlavə edin"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni istifadəçi"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Qonaq silinsin?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Qonaq sessiyası sıfırlansın?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Yığışdır"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Sıfırlayın"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xoş gəlmisiniz!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string> @@ -605,15 +607,15 @@ <string name="accessibility_output_chooser" msgid="7807898688967194183">"Çıxış cihazına keçin"</string> <string name="screen_pinning_title" msgid="9058007390337841305">"Tətbiq bərkidilib"</string> <string name="screen_pinning_description" msgid="8699395373875667743">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri və İcmal düymələrinə basıb saxlayın."</string> - <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri və Əsas səhifə düymələrinə basıb saxlayın."</string> - <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Bu, onu çıxarana qədər görünəcək. Çıxarmaq üçün yuxarı sürüşdürün & basıb saxlayın."</string> - <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri düyməsinə basıb saxlayın."</string> - <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Əsas səhifə düyməsinə basıb saxlayın."</string> - <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Şəxsi məlumatlar (məsələn, kontaktlar və e-poçt məzmunu) əlçatan ola bilər."</string> + <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"\"Geri\" və \"Əsas ekran\" düymələrinin davamlı basılması ilə çıxarılana qədər tətbiq göz önündə qalır."</string> + <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Yuxarı sürüşdürülüb saxlanılana ilə çıxarılana qədər tətbiq göz önündə qalır."</string> + <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"\"İcmal\" düyməsinin davamlı basılması ilə çıxarılana qədər tətbiq göz önündə qalır."</string> + <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"\"Əsas ekran\" düyməsinin davamlı basılması ilə çıxarılana qədər tətbiq göz önündə qalır."</string> + <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Şəxsi məlumatlar (məsələn, kontaktlar və e-poçt məzmunu) ekranda görünə bilər."</string> <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Bərkidilmiş tətbiq digər tətbiqləri aça bilər."</string> <string name="screen_pinning_toast" msgid="8177286912533744328">"Bu tətbiqi çıxarmaq üçün Geri və İcmal düymələrinə basıb saxlayın"</string> <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Bu tətbiqi çıxarmaq üçün Geri və Əsas ekran düymələrinə basıb saxlayın"</string> - <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Bu tətbiqi çıxarmaq üçün yuxarı sürüşdürüb saxlayın"</string> + <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Tətbiqi çıxarmaq üçün yuxarı sürüşdürüb saxlayın"</string> <string name="screen_pinning_positive" msgid="3285785989665266984">"Anladım!"</string> <string name="screen_pinning_negative" msgid="6882816864569211666">"Yox, çox sağ olun"</string> <string name="screen_pinning_start" msgid="7483998671383371313">"Tətbiq bərkidildi"</string> @@ -1033,7 +1035,8 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Tam ekranı böyüdün"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran hissəsinin böyüdülməsi"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Dəyişdirici"</string> - <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Əlçatımlılıq düyməsi əlçatımlılıq jestini əvəz etdi\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string> + <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Jest xüsusi imkanlar düyməsinə dəyişdirildi\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Əlçatımlılıq jestindən düymə\n\n"<annotation id="link">"Ayarlarına"</annotation>" keçə bilərsiniz"</string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düyməni müvəqqəti gizlətmək üçün kənara çəkin"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuxarıya sola köçürün"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuxarıya sağa köçürün"</string> @@ -1043,7 +1046,7 @@ <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Kənara daşıyıb göstərin"</string> <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"keçirin"</string> <string name="quick_controls_title" msgid="7095074621086860062">"Əsas səhifə nizamlayıcıları"</string> - <string name="controls_providers_title" msgid="6879775889857085056">"Nizamlayıcıları əlavə etmək üçün tətbiq seçin"</string> + <string name="controls_providers_title" msgid="6879775889857085056">"Kontrol əlavə etmək üçün tətbiq seçin"</string> <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380"> <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> nizamlayıcı əlavə edilib.</item> <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> nizamlayıcı əlavə edilib.</item> @@ -1057,8 +1060,8 @@ <string name="accessibility_control_move" msgid="8980344493796647792">"<xliff:g id="NUMBER">%d</xliff:g> mövqeyinə keçirin"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"Nizamlayıcılar"</string> <string name="controls_favorite_subtitle" msgid="6481675111056961083">"Sürətli Ayarlardan giriş üçün nizamlayıcıları seçin"</string> - <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Nizamlayıcıları yenidən tənzimləmək üçün tutub sürüşdürün"</string> - <string name="controls_favorite_removed" msgid="5276978408529217272">"Bütün nizamlayıcılar silindi"</string> + <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Vidcetləri daşıyaraq yerini dəyişin"</string> + <string name="controls_favorite_removed" msgid="5276978408529217272">"Kontrol vidcetləri silindi"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Dəyişikliklər yadda saxlanmadı"</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Digər tətbiqlərə baxın"</string> <string name="controls_favorite_load_error" msgid="5126216176144877419">"Nizamlayıcıları yükləmək mümkün olmadı. <xliff:g id="APP">%s</xliff:g> tətbiqinə toxunaraq tətbiq ayarlarının dəyişmədiyinə əmin olun."</string> @@ -1100,8 +1103,8 @@ <string name="controls_error_failed" msgid="960228639198558525">"Xəta, yenidən cəhd edin"</string> <string name="controls_in_progress" msgid="4421080500238215939">"Davam edir"</string> <string name="controls_added_tooltip" msgid="5866098408470111984">"Yeni nizamlayıcılara baxmaq üçün Sürətli Ayarları açın"</string> - <string name="controls_menu_add" msgid="4447246119229920050">"Nizamlayıcılar əlavə edin"</string> - <string name="controls_menu_edit" msgid="890623986951347062">"Nizamlayıcıları redaktə edin"</string> + <string name="controls_menu_add" msgid="4447246119229920050">"Vidcet əlavə edin"</string> + <string name="controls_menu_edit" msgid="890623986951347062">"Vidcetlərə düzəliş edin"</string> <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Nəticələri əlavə edin"</string> <string name="media_output_dialog_group" msgid="5571251347877452212">"Qrup"</string> <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 cihaz seçilib"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 677bfb2d51a9..fe89267456a9 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -482,8 +482,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li da uklonite gosta?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli nazad, goste!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string> @@ -1039,6 +1043,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećajte deo ekrana"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pređi"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Dugme Pristupačnost je zamenilo pokret za pristupačnost\n\n"<annotation id="link">"Prikaži podešavanja"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Možete da pređete sa pokreta za pristupačnost na dugme\n\n"<annotation id="link">"Podešavanja"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomerite dugme do ivice da biste ga privremeno sakrili"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premesti gore levo"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premesti gore desno"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 6576a920e302..5afd979d34f5 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -484,8 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Дадаць карыстальніка"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новы карыстальнік"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Выдаліць госця?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Скінуць гасцявы сеанс?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Выдаліць"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Скінуць"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"З вяртаннем, госць!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string> @@ -1044,6 +1046,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Павялічыць частку экрана"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пераключальнік"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Замест жэста спецыяльных магчымасцей будзе выкарыстоўвацца кнопка\n\n"<annotation id="link">"Праглядзець налады"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Вы можаце пераключыцца з жэста спецыяльных магчымасцей на кнопку\n\n"<annotation id="link">"Налады"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Каб часова схаваць кнопку, перамясціце яе на край"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перамясціць лявей і вышэй"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перамясціць правей і вышэй"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index d51bc7b21318..3f9269af1f50 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Добавяне на потребител"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нов потребител"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се премахне ли гостът?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Да се нулира ли сесията като гост?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Премахване"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Нулиране"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дошли отново в сесията като гост!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Искате ли да продължите сесията си?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Започване отначало"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличаване на част от екрана"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Превключване"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Жестът за достъпност бе заменен от бутон\n\n"<annotation id="link">"Преглед на настройките"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Вместо жеста за достъпност можете да използвате бутон\n\n"<annotation id="link">"Настройки"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете бутона до края, за да го скриете временно"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Преместване горе вляво"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Преместване горе вдясно"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index b402402e7de4..0eedd1ca13f1 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"ব্যবহারকারী জুড়ুন"</string> <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যবহারকারী"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি সরাবেন?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"সরান"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"অতিথি, আপনি ফিরে আসায় আপনাকে স্বাগত!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি চালিয়ে যেতে চান?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"বদল করুন"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"অ্যাক্সেসিবিলিটি জেসচার পরিবর্তন করে অ্যাক্সেসেবিলিটি বোতাম করা হয়েছে\n\n"<annotation id="link">"সেটিংস দেখুন"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"আপনি অ্যাক্সেসিবিলিটি জেসচারের বদলে \n\n"<annotation id="link">"সেটিংস"</annotation>" বোতামে সুইচ করতে পারেন"</string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"এটি অস্থায়ীভাবে লুকাতে বোতামটি কোণে সরান"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"উপরে বাঁদিকে সরান"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"উপরে ডানদিকে সরান"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index cfcceb69f1e2..615d34634789 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -482,8 +482,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Poništiti gostujuću sesiju?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Poništi"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Zdravo! Lijepo je opet vidjeti goste."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> @@ -1039,6 +1041,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećavanje dijela ekrana"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prekidač"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Dugme za pristupačnost je zamijenilo pokret za pristupačnost\n\n"<annotation id="link">"Prikaži postavke"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Možete prebaciti s pokreta za pristupačnost na dugme\n\n"<annotation id="link">"Postavke"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Premjestite dugme do ivice da ga privremeno sakrijete"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pomjeranje gore lijevo"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pomjeranje gore desno"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 925a293b191b..2428d55fa1e6 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Afegeix un usuari"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Usuari nou"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vols suprimir el convidat?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Suprimeix"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvingut de nou, convidat."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplia una part de la pantalla"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Canvia"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"El gest d\'accessibilitat s\'ha substituït pel botó d\'accessibilitat\n\n"<annotation id="link">"Mostra la configuració"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Pots canviar del gest d\'accessibilitat a un botó\n\n"<annotation id="link">"Configuració"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mou el botó a l\'extrem per amagar-lo temporalment"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mou a dalt a l\'esquerra"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mou a dalt a la dreta"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index f3d64ab56a39..d8e0a6a1d446 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -484,8 +484,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Přidat uživatele"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový uživatel"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstranit hosta?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstranit"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Vítejte zpět v relaci hosta!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string> @@ -1044,6 +1048,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zvětšit část obrazovky"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Přepnout"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tlačítko přístupnosti bylo nahrazeno gestem přístupnosti\n\n"<annotation id="link">"Zobrazit nastavení"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Z gesta přístupnosti můžete přejít na tlačítko\n\n"<annotation id="link">"Nastavení"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Přesunutím tlačítka k okraji ho dočasně skryjete"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Přesunout vlevo nahoru"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Přesunout vpravo nahoru"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 0c7a7439a784..ca44f0a350d7 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Tilføj bruger"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruger"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gæsten?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbage, gæst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstør en del af skærmen"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Skift"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Knappen Hjælpefunktioner har erstattet bevægelsen for hjælpefunktioner\n\n"<annotation id="link">"Se indstillinger"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Hvis du ikke vil bruge bevægelsen til hjælpefunktioner, kan du skifte til knappen\n\n"<annotation id="link">"Indstillinger"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flyt knappen til kanten for at skjule den midlertidigt"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flyt op til venstre"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flyt op til højre"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 0f18757975fd..17cb3b9f3c32 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Nutzer hinzufügen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Neuer Nutzer"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast entfernen?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Entfernen"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Willkommen zurück im Gastmodus"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schalter"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Die Schaltfläche „Bedienungshilfen“ ersetzt die Touch-Geste für Bedienungshilfen\n\n"<annotation id="link">"Einstellungen aufrufen"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Du kannst von der barrierefreien Geste zu einer Schaltfläche wechseln\n\n"<annotation id="link">"Einstellungen"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Durch Ziehen an den Rand wird die Schaltfläche zeitweise ausgeblendet"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Nach oben links verschieben"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Nach rechts oben verschieben"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 9525f99d5948..7b135a3e32da 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Προσθήκη χρήστη"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Νέος χρήστης"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Κατάργηση επισκέπτη;"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Κατάργηση"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Kαλώς ορίσατε ξανά!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Εναλλαγή"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Το κουμπί προσβασιμότητας αντικατέστησε την κίνηση προσβασιμότητας\n\n"<annotation id="link">"Προβολή ρυθμίσεων"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Μπορείτε να κάνετε εναλλαγή από την κίνηση προσβασιμότητας σε ένα κουμπί\n\n"<annotation id="link">"Ρυθμίσεις"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Μετακινήστε το κουμπί στο άκρο για προσωρινή απόκρυψη"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Μετακίνηση επάνω αριστερά"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Μετακίνηση επάνω δεξιά"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index c346bbe68438..d7b39e260092 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reset guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reset"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index b588e861639a..0a49742058bf 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reset guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reset"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index c346bbe68438..d7b39e260092 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reset guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reset"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index c346bbe68438..d7b39e260092 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reset guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reset"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index a73c3878eaae..ca3d72f6e589 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reset guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reset"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start over"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation>""</string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation>""</string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 051c0ddd687d..0479ad0fcb85 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Agregar usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Usuario nuevo"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Quitar invitado?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Hola de nuevo, invitado!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres retomar la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botón"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"El botón de accesibilidad reemplaza el gesto de accesibilidad\n\n"<annotation id="link">"Ver configuración"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puedes cambiar de un gesto a un botón de accesibilidad\n\n"<annotation id="link">"Configuración"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index d04fd45b50dd..65648153e8f9 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Añadir usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuevo usuario"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Quitar invitado?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hola de nuevo, invitado"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"El botón Accesibilidad ha reemplazado el gesto de accesibilidad\n\nVer ajustes"<annotation id="link"></annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puedes cambiar el gesto de accesibilidad por un botón\n\n"<annotation id="link">"Ajustes"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 00d98c256567..497845707e46 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Lisa kasutaja"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uus kasutaja"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Kas eemaldada külaline?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Kas lähtestada külastajaseanss?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eemalda"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Lähtesta"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tere tulemast tagasi, külaline!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaheta"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Juurdepääsetavuse nupp asendas juurdepääsuliigutuse\n\n"<annotation id="link">"Vaadake seadeid"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Saate juurdepääsuliigutuselt nupule lülituda.\n\n"<annotation id="link">"Seaded"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Teisaldage nupp serva, et see ajutiselt peita"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Teisalda üles vasakule"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Teisalda üles paremale"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 78eb7289c786..6d8a8fdfad0c 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -160,7 +160,7 @@ <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Erabili PINa"</string> <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Erabili eredua"</string> <string name="biometric_dialog_use_password" msgid="3445033859393474779">"Erabili pasahitza"</string> - <string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN kodea ez da zuzena"</string> + <string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PINa ez da zuzena"</string> <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Eredua ez da zuzena"</string> <string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Pasahitza ez da zuzena"</string> <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Saiakera oker gehiegi egin dituzu.\nSaiatu berriro <xliff:g id="NUMBER">%d</xliff:g> segundo barru."</string> @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Gehitu erabiltzaile bat"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Erabiltzaile berria"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gonbidatua kendu nahi duzu?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kendu"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Ongi etorri berriro, gonbidatu hori!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Saioarekin jarraitu nahi duzu?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Hasi berriro"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botoia"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Erabilerraztasuna botoiak erabilerraztasun-keinua ordezkatu du\n\n"<annotation id="link">"Ikusi ezarpenak"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Erabilerraztasun-keinua erabiltzeari utz diezaiokezu eta botoi bat erabiltzen has zaitezke\n\n"<annotation id="link">"Ezarpenak"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Eraman botoia ertzera aldi baterako ezkutatzeko"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Eraman goialdera, ezkerretara"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Eraman goialdera, eskuinetara"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 10e95f4be3bc..ce3e676db760 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"افزودن کاربر"</string> <string name="user_new_user_name" msgid="2019166282704195789">"کاربر جدید"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مهمان حذف شود؟"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامهها و دادههای این جلسه حذف خواهد شد."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"حذف"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مهمان گرامی، بازگشتتان را خوش آمد میگوییم!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا میخواهید جلسهتان را ادامه دهید؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"درشتنمایی بخشی از صفحه"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"کلید"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"دکمه دسترسپذیری جایگزین اشاره دسترسپذیری شد\n\n"<annotation id="link">"مشاهده تنظیمات"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"میتوانید بهجای اشاره دسترسپذیری از دکمه استفاده کنید\n\n"<annotation id="link">"تنظیمات"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"برای پنهان کردن موقتی دکمه، آن را به لبه ببرید"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"انتقال به بالا سمت راست"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"انتقال به بالا سمت چپ"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 51b73df555ec..90f47ef42a5a 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Lisää käyttäjä"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uusi käyttäjä"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Poistetaaanko vieras?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Poista"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tervetuloa takaisin!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Suurenna osa näytöstä"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaihda"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Esteettömyyspainike on korvannut esteettömyyseleen\n\n"<annotation id="link">"Katso asetukset"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Voit vaihtaa käyttämään esteettömyyseleen sijaan painiketta\n\n"<annotation id="link">"Asetukset"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Piilota painike tilapäisesti siirtämällä se reunaan"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Siirrä vasempaan yläreunaan"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Siirrä oikeaan yläreunaan"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index e657308da857..030428034545 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Commutateur"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Le bouton d\'accessibilité a remplacé le geste d\'accessibilité\n\n"<annotation id="link">"Voir les paramètres"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Vous pouvez passer du geste d\'accessibilité au bouton\n\n"<annotation id="link">"Paramètres"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacez le bouton vers le bord pour le masquer temporairement"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer dans coin sup. gauche"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer dans coin sup. droit"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 04f7692c5720..c4022611f897 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité ?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Changer"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Le bouton Accessibilité a remplacé le geste d\'accessibilité\n\n"<annotation id="link">"Afficher les paramètres"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Vous pouvez passer du geste d\'accessibilité à un bouton\n\n"<annotation id="link">"Paramètres"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacer le bouton vers le bord pour le masquer temporairement"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer en haut à gauche"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer en haut à droite"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 52c6e3360cf2..e7b3e925d5e2 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Engadir usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuario"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Queres quitar o convidado?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvido de novo, convidado"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botón de accesibilidade substituíu o xesto de accesibilidade\n\n"<annotation id="link">"Ver configuración"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Podes cambiar do xesto de accesibilidade a un botón\n\n"<annotation id="link">"Configuración"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Para ocultar temporalmente o botón, móveo ata o bordo"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover á parte super. esquerda"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover á parte superior dereita"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index c06826ba65c4..a99558e4d84e 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"વપરાશકર્તા ઉમેરો"</string> <string name="user_new_user_name" msgid="2019166282704195789">"નવો વપરાશકર્તા"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"અતિથિ દૂર કરીએ?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"અતિથિ સત્રને રીસેટ કરીએ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"દૂર કરો"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"રીસેટ કરો"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ફરી સ્વાગત છે, અતિથિ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ રાખવા માંગો છો?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string> @@ -1034,6 +1036,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"સ્વિચ"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ઍક્સેસિબિલિટી સંકેતને ઍક્સેસિબિલિટી બટન વડે બદલવામાં આવ્યા છે\n\n"<annotation id="link">"સેટિંગ જુઓ"</annotation></string> + <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> + <skip /> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"તેને હંગામી રૂપે ખસેડવા માટે બટનને કિનારી પર ખસેડો"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ઉપર ડાબે ખસેડો"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ઉપર જમણે ખસેડો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index cdead4108f1c..4196b1f41d86 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"उपयोगकर्ता जोड़ें"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नया उपयोगकर्ता"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"क्या आप मेहमान को हटाना चाहते हैं?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सत्र के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"निकालें"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"मेहमान, आपका फिर से स्वागत है!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"क्या आप अपना सत्र जारी रखना चाहते हैं?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीन के किसी हिस्से को ज़ूम करें"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"सुलभता वाले हाथ के जेस्चर (हाव-भाव) को सुलभता बटन से बदल दिया गया है\n\n"<annotation id="link">"सेटिंग देखें"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"आप सुलभता वाले हाथ के जेस्चर (हाव-भाव) के बजाय, बटन का इस्तेमाल कर सकते हैं\n\n"<annotation id="link">"सेटिंग"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटन को कुछ समय छिपाने के लिए, उसे किनारे पर ले जाएं"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सबसे ऊपर बाईं ओर ले जाएं"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सबसे ऊपर दाईं ओर ले जाएं"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 14250a774349..abbbae7985d4 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -482,8 +482,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodavanje korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Poništiti gostujuću sesiju?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji bit će izbrisani."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Poništi"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli natrag, gostu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> @@ -1039,6 +1041,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prebacivanje"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Gumb za Pristupačnost zamijenio je pokret pristupačnosti\n\n"<annotation id="link">"Prikaz postavki"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Možete prijeći s pokreta za pristupačnost na gumb\n\n"<annotation id="link">"Postavke"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomaknite gumb do ruba da biste ga privremeno sakrili"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premjesti u gornji lijevi kut"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premjesti u gornji desni kut"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 2022281c890e..15106bb4c157 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Felhasználó hozzáadása"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Új felhasználó"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Eltávolítja a vendég munkamenetet?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Visszaállítja a vendég munkamenetet?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eltávolítás"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Visszaállítás"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Örülünk, hogy visszatért, vendég!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Képernyő bizonyos részének nagyítása"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Váltás"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"A kisegítő kézmozdulat helyébe a Kisegítő lehetőségek gomb lépett\n\n"<annotation id="link">"Beállítások megtekintése"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Átválthat kisegítő kézmozdulatról gomb használatára\n\n"<annotation id="link">"Beállítások"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"A gombot a szélre áthelyezve ideiglenesen elrejtheti"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Áthelyezés fel és balra"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Áthelyezés fel és jobbra"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 4fb191f93035..2e2141ee075c 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Ավելացնել օգտատեր"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Նոր օգտատեր"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Հեռացնե՞լ հյուրին"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Վերակայե՞լ հյուրի աշխատաշրջանը"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր ծրագրերն ու տվյալները կջնջվեն:"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Հեռացնել"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Վերակայել"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Բարի վերադարձ, հյուր"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Շարունակե՞լ աշխատաշրջանը։"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Վերսկսել"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Խոշորացնել էկրանի որոշակի հատվածը"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Փոխել"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Հատուկ գործառույթների ժեստը փոխարինվել է կոճակով\n\n"<annotation id="link">"Բացել կարգավորումները"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Դուք հատուկ գործառույթի ժեստի փոխարեն կարող եք անցնել համապատասխան կոճակի օգտագործմանը\n\n"<annotation id="link">"Կարգավորումներ"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Կոճակը ժամանակավորապես թաքցնելու համար այն տեղափոխեք էկրանի եզր"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Տեղափոխել վերև՝ ձախ"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Տեղափոխել վերև՝ աջ"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 7e4ba76c340c..53a3165776fd 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Tambahkan pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baru"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Hapus tamu?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reset sesi tamu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data di sesi ini akan dihapus."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hapus"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reset"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat datang kembali, tamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Perbesar sebagian layar"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Alihkan"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tombol aksesibilitas menggantikan gestur aksesibilitas\n\n"<annotation id="link">"Tampilkan setelan"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Anda dapat beralih dari gestur aksesibilitas ke tombol\n\n"<annotation id="link">"Setelan"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pindahkan tombol ke tepi agar tersembunyi untuk sementara"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pindahkan ke kiri atas"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pindahkan ke kanan atas"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 229bcbb344bd..01bad1c1ce90 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Bæta notanda við"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nýr notandi"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Fjarlægja gest?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjarlægja"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkominn aftur, gestur!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Stækka hluta skjásins"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Rofi"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Aðgengishnappur kom í stað aðgengisbendingar\n\n"<annotation id="link">"Skoða stillingar"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Þú getur skipt úr aðgengisbendingu yfir í hnapp\n\n"<annotation id="link">"Stillingar"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Færðu hnappinn að brúninni til að fela hann tímabundið"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Færa efst til vinstri"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Færa efst til hægri"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 6c1ef8aa7294..454b5bba4a49 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Aggiungi utente"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuovo utente"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Rimuovere l\'ospite?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reimpostare sessione Ospite?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Rimuovi"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reimposta"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Ti ridiamo il benvenuto alla sessione Ospite."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ingrandisci parte dello schermo"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Opzione"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Il pulsante Accessibilità ha sostituito il gesto di accessibilità\n\n"<annotation id="link">"Visualizza le impostazioni"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puoi passare dal gesto di accessibilità a un pulsante\n\n"<annotation id="link">"Impostazioni"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sposta il pulsante fino al bordo per nasconderlo temporaneamente"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sposta in alto a sinistra"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sposta in alto a destra"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index aae58cbbce87..32c386d9993e 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -484,8 +484,12 @@ <string name="user_add_user" msgid="4336657383006913022">"הוספת משתמש"</string> <string name="user_new_user_name" msgid="2019166282704195789">"משתמש חדש"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"להסיר אורח?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בסשן הזה יימחקו."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"הסרה"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"שמחים לראותך שוב!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"סשן חדש"</string> @@ -721,7 +725,7 @@ <string name="notification_channel_unsilenced" msgid="94878840742161152">"ההודעות אלה יישלחו כהתראות"</string> <string name="inline_blocking_helper" msgid="2891486013649543452">"ההתראות האלה בדרך כלל נדחות על ידך. \nלהמשיך להציג אותן?"</string> <string name="inline_done_button" msgid="6043094985588909584">"סיום"</string> - <string name="inline_ok_button" msgid="603075490581280343">"החלה"</string> + <string name="inline_ok_button" msgid="603075490581280343">"אישור"</string> <string name="inline_keep_showing" msgid="8736001253507073497">"שנמשיך להציג לך את ההתראות האלה?"</string> <string name="inline_stop_button" msgid="2453460935438696090">"לא, אל תמשיכו"</string> <string name="inline_deliver_silently_button" msgid="2714314213321223286">"הצגה ללא צליל"</string> @@ -909,7 +913,7 @@ <string-array name="clock_options"> <item msgid="3986445361435142273">"הצגת שעות, דקות ושניות"</item> <item msgid="1271006222031257266">"הצגת שעות ודקות (ברירת מחדל)"</item> - <item msgid="6135970080453877218">"לא להציג את הסמל הזה"</item> + <item msgid="6135970080453877218">"בלי הסמל הזה"</item> </string-array> <string-array name="battery_options"> <item msgid="7714004721411852551">"תמיד להציג באחוזים"</item> @@ -1044,6 +1048,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"הגדלת חלק מהמסך"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"מעבר"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"לחצן הנגישות החליף את תנועת הנגישות\n\n"<annotation id="link">"להצגת ההגדרות"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ניתן להחליף את תנועת הנגישות בלחצן\n\n"<annotation id="link">"הגדרות"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"כדי להסתיר זמנית את הלחצן, יש להזיז אותו לקצה"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"העברה לפינה השמאלית העליונה"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"העברה לפינה הימנית העליונה"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 2ba73b365036..dacdf2146540 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ユーザーを追加"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新しいユーザー"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ゲストを削除しますか?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ゲストをリセットしますか?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"削除"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"リセット"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"おかえりなさい、ゲストさん"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"画面の一部を拡大します"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"スイッチ"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ユーザー補助ジェスチャーに代わって、ユーザー補助機能ボタンが導入されました\n\n"<annotation id="link">"設定を表示"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ユーザー補助操作からボタンに切り替えることができます\n\n"<annotation id="link">"設定"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ボタンを一時的に非表示にするには、端に移動させてください"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"左上に移動"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"右上に移動"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 1f071a209377..06af42adff22 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"მომხმარებლის დამატება"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ახალი მომხმარებელი"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"სტუმრის ამოშლა?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"გადაყენდეს სტუმარი?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ამოშლა"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"გადაყენება"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"სტუმარო, გვიხარია, რომ დაბრუნდით!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"გსურთ, თქვენი სესიის გაგრძელება?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ხელახლა დაწყება"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ეკრანის ნაწილის გადიდება"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"გადართვა"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"მარტივი წვდომის ღილაკმა ჩაანაცვლა მარტივი წვდომის ჟესტი\n\n"<annotation id="link">"პარამეტრების ნახვა"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"შეგიძლიათ, გადართოთ მარტივი წვდომის ჟესტიდან ღილაკის\n\n"<annotation id="link">"პარამეტრებზე"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"გადაიტანეთ ღილაკი კიდეში, რათა დროებით დამალოთ ის"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ზევით და მარცხნივ გადატანა"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ზევით და მარჯვნივ გადატანა"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 9ab5d80b1900..8366b2f48927 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Пайдаланушы қосу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңа пайдаланушы"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Қонақты жою керек пе?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Қонақ сеансы бастапқы күйге қайтарылсын ба?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданбалар мен деректер жойылады."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып тастау"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Бастапқы күйге қайтару"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Қош келдіңіз, қонақ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string> @@ -664,7 +666,7 @@ <string name="enable_demo_mode" msgid="3180345364745966431">"Демо режимін қосу"</string> <string name="show_demo_mode" msgid="3677956462273059726">"Демо режимін көрсету"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> - <string name="status_bar_alarm" msgid="87160847643623352">"Дабыл"</string> + <string name="status_bar_alarm" msgid="87160847643623352">"Оятқыш"</string> <string name="wallet_title" msgid="5369767670735827105">"Әмиян"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Телефоныңызбен бұрынғыдан да жылдам әрі қауіпсіз сатып алу үшін параметрлерді орнатыңыз."</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Барлығын көрсету"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экранның бөлігін ұлғайту"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ауысу"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Арнайы мүмкіндіктер қимылының орнына \"Арнайы мүмкіндіктер\" түймесі болады.\n\n"<annotation id="link">"Параметрлерді көру"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Арнайы мүмкіндіктер қимылынан арнайы мүмкіндіктер түймесіне ауысуыңызға болады.\n\n"<annotation id="link">"Параметрлер"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Түймені уақытша жасыру үшін оны шетке қарай жылжытыңыз."</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жоғарғы сол жаққа жылжыту"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жоғарғы оң жаққа жылжыту"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 6ce7300f4a73..3baa4e1411f1 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"បញ្ចូលអ្នកប្រើ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"អ្នកប្រើថ្មី"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ដកភ្ញៀវចេញឬ?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"កំណត់ភ្ញៀវឡើងវិញឬ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យទាំងអស់ក្នុងវគ្គនេះនឹងត្រូវលុប។"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ដកចេញ"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"កំណត់ឡើងវិញ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"សូមស្វាគមន៍ការត្រឡប់មកវិញ, ភ្ញៀវ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"តើអ្នកចង់បន្តវគ្គរបស់អ្នកទេ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើមសាជាថ្មី"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ពង្រីកផ្នែកនៃអេក្រង់"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ប៊ូតុងបិទបើក"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ប៊ូតុងភាពងាយស្រួលបានជំនួសចលនាភាពងាយស្រួល\n\n"<annotation id="link">"មើលការកំណត់"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"អ្នកអាចប្ដូរពីចលនាភាពងាយស្រួលទៅប៊ូតុង\n\n"<annotation id="link">"ការកំណត់"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ផ្លាស់ទីប៊ូតុងទៅគែម ដើម្បីលាក់វាជាបណ្ដោះអាសន្ន"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងឆ្វេង"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងស្ដាំ"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 78d086f3ab24..0b21c0f37010 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ಹೊಸ ಬಳಕೆದಾರರು"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಷನ್ನಲ್ಲಿನ ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ತೆಗೆದುಹಾಕಿ"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ಮತ್ತೆ ಸುಸ್ವಾಗತ, ಅತಿಥಿ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ಸ್ಕ್ರೀನ್ನ ಅರ್ಧಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ಸ್ವಿಚ್"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್, ಪ್ರವೇಶಿಸುವಿಕೆ ಗೆಸ್ಚರ್ ಅನ್ನು ಬದಲಾಯಿಸಿದೆ\n\n"<annotation id="link">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ನೀವು ಪ್ರವೇಶಿಸುವಿಕೆ ಗೆಸ್ಚರ್ನಿಂದ ಬಟನ್ಗೆ ಬದಲಾಯಿಸಬಹುದು\n\n"<annotation id="link">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ಅದನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ಮರೆಮಾಡಲು ಅಂಚಿಗೆ ಬಟನ್ ಸರಿಸಿ"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ಎಡ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 819250c47cc0..ab80f7f3acdc 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string> <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"게스트를 초기화하시겠습니까?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"초기화"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"게스트 세션 진행"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"화면 일부 확대"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"전환"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"접근성 동작이 접근성 버튼으로 대체되었습니다\n\n"<annotation id="link">"설정 보기"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"접근성 동작을 버튼으로 전환할 수 있습니다.\n\n"<annotation id="link">"설정"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"버튼을 가장자리로 옮겨서 일시적으로 숨기세요"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"왼쪽 상단으로 이동"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"오른쪽 상단으로 이동"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index b5bbf2163352..037a765870d4 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Колдонуучу кошуу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңы колдонуучу"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Конокту алып саласызбы?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Конок сеансын баштапкы абалга келтиресизби?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана маалыматтар өчүрүлөт."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Өчүрүү"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Баштапкы абалга келтирүү"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Кайтып келишиңиз менен!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экрандын бир бөлүгүн чоңойтуу"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Которулуу"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Атайын мүмкүнчүлүктөр жаңсоосунун ордуна атайын мүмкүнчүлүктөр баскычы колдонулмакчы\n\n"<annotation id="link">"Жөндөөлөрдү көрүү"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Атайын мүмкүнчүлүктөр жаңсоосунан баскычка которула аласыз\n\n"<annotation id="link">"Жөндөөлөр"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Баскычты убактылуу жашыра туруу үчүн экрандын четине жылдырыңыз"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жогорку сол жакка жылдыруу"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жогорку оң жакка жылдырыңыз"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index ca4122ee02fd..4cdc8a5fcb4e 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ເພີ່ມຜູ້ໃຊ້"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ຜູ່ໃຊ້ໃໝ່"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ລຶບແຂກບໍ?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ຣີເຊັດແຂກບໍ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯແລະຂໍ້ມູນທັງໝົດໃນເຊດຊັນນີ້ຈະຖືກລຶບອອກ."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ລຶບ"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ຣີເຊັດ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ຍິນດີຕ້ອນຮັບກັບມາ, ຜູ້ຢ້ຽມຢາມ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານຕ້ອງການສືບຕໍ່ເຊດຊັນຂອງທ່ານບໍ່?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ຂະຫຍາຍບາງສ່ວນຂອງໜ້າຈໍ"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ສະຫຼັບ"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ປຸ່ມການຊ່ວຍເຂົ້າເຖິງຖືກແທນທີ່ທ່າທາງຊ່ວຍເຂົ້າເຖິງແລ້ວ\n\n"<annotation id="link">"ເບິ່ງການຕັ້ງຄ່າ"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ທ່ານສາມາດສະຫຼັບທ່າທາງການຊ່ວຍເຂົ້າເຖິງເປັນປຸ່ມໄດ້\n\n"<annotation id="link">"ການຕັ້ງຄ່າ"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ຍ້າຍປຸ່ມໄປໃສ່ຂອບເພື່ອເຊື່ອງມັນຊົ່ວຄາວ"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ຍ້າຍຊ້າຍເທິງ"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ຍ້າຍຂວາເທິງ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 65a41bb5cf65..44dc8bb6aa30 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -484,8 +484,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Pridėti naudotoją"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Naujas naudotojas"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Pašalinti svečią?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Pašalinti"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Sveiki sugrįžę, svety!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string> @@ -1044,6 +1048,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Didinti ekrano dalį"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Perjungti"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Pritaikomumo gestas pakeistas pritaikomumo mygtuku\n\n"<annotation id="link">"Žr. nustatymus"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Galite pereiti nuo pritaikomumo gesto prie mygtuko.\n\n"<annotation id="link">"Nustatymai"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Perkelkite mygtuką prie krašto, kad laikinai jį paslėptumėte"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Perkelti į viršų kairėje"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Perkelti į viršų dešinėje"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 13de03623ab2..b846be4491f3 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -482,8 +482,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Lietotāja pievienošana"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Jauns lietotājs"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vai noņemt viesi?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Noņemt"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Laipni lūdzam atpakaļ, viesi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string> @@ -1039,6 +1043,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Palielināt ekrāna daļu"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pārslēgt"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Pieejamības žests ir aizstāts ar pieejamības pogu\n\n"<annotation id="link">"Skatīt iestatījumus"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Varat pārslēgties no pieejamības žesta uz pogu.\n\n"<annotation id="link">"Iestatījumi"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Lai īslaicīgi paslēptu pogu, pārvietojiet to uz malu"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pārvietot augšpusē pa kreisi"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pārvietot augšpusē pa labi"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index b70e2e56761d..90f775bcd310 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Додај корисник"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нов корисник"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се отстрани гостинот?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Да се ресетира гостинот?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Отстрани"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Ресетирај"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дојде пак, гостине!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Зголемувајте дел од екранот"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Префрли"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Копчето за пристапност го замени движењето за пристапност\n\n"<annotation id="link">"Прикажи поставки"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Може да го смените движењето за пристапност во копче\n\n"<annotation id="link">"Поставки"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете го копчето до работ за да го сокриете привремено"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 746d6f45accd..74d087c73d39 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"ഉപയോക്താവിനെ ചേര്ക്കുക"</string> <string name="user_new_user_name" msgid="2019166282704195789">"പുതിയ ഉപയോക്താവ്"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"അതിഥിയെ നീക്കംചെയ്യണോ?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"നീക്കംചെയ്യുക"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"അതിഥി, വീണ്ടും സ്വാഗതം!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"സ്ക്രീനിന്റെ ഭാഗം മാഗ്നിഫൈ ചെയ്യുക"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"മാറുക"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ഉപയോഗസഹായി ജെസ്ച്ചറിനെ മാറ്റി പകരം ഉപയോഗസഹായി ബട്ടൺ വന്നു\n\n"<annotation id="link">"ക്രമീകരണം കാണുക"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"നിങ്ങൾക്ക് ഉപയോഗസഹായി ജെസ്ച്ചറിൽ നിന്ന് ഒരു ബട്ടണിലേക്ക് മാറാനാകും\n\n"<annotation id="link">"ക്രമീകരണം"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"തൽക്കാലം മറയ്ക്കുന്നതിന് ബട്ടൺ അരുകിലേക്ക് നീക്കുക"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"മുകളിൽ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 9732fbd27470..976c2dd420c8 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Хэрэглэгч нэмэх"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Шинэ хэрэглэгч"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Зочныг хасах уу?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Зочныг шинэчлэх үү?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Хасах"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Шинэчлэх"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Эргэн тавтай морилно уу!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Дэлгэцийн нэг хэсгийг томруулах"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Сэлгэх"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Хандалтын товчлуурыг хандалтын зангаагаар сольсон\n\n"<annotation id="link">"Тохиргоо харах"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Та хандалтын зангаанаас товчлуур луу сэлгэх боломжтой\n\n"<annotation id="link">"Тохиргоо"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Үүнийг түр нуухын тулд товчлуурыг зах руу зөөнө үү"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Зүүн дээш зөөх"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Баруун дээш зөөх"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index cc7f74055054..84b559475019 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"वापरकर्ता जोडा"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नवीन वापरकर्ता"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथी काढायचे?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"काढा"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथी, तुमचे पुन्हा स्वागत आहे!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"अॅक्सेसिबिलिटी जेश्चर हे आता अॅक्सेसिबिलिटी बटण आहे \n\n"<annotation id="link">"सेटिंग्ज पाहा"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"तुम्ही अॅक्सेसिबिलिटी जेश्चरवरून बटणवर स्विच करू शकता \n\n"<annotation id="link">"सेटिंग्ज"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटण तात्पुरते लपवण्यासाठी ते कोपर्यामध्ये हलवा"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"वर डावीकडे हलवा"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"वर उजवीकडे हलवा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 1f97ef6ea74d..f3838c4ce802 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Tambah pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baharu"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alih keluar tetamu?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alih keluar"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat kembali, tetamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Besarkan sebahagian skrin"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Tukar"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Butang kebolehaksesan menggantikan gerak isyarat kebolehaksesan\n\n"<annotation id="link">"Lihat tetapan"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Anda boleh beralih daripada gerak isyarat kebolehaksesan kepada butang\n\n"<annotation id="link">"Tetapan"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Gerakkan butang ke tepi untuk disembunyikan buat sementara waktu"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Alihkan ke atas sebelah kiri"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Alihkan ke atas sebelah kanan"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 78bc9918331c..efe716e9d04a 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"အသုံးပြုသူ ထည့်ရန်"</string> <string name="user_new_user_name" msgid="2019166282704195789">"အသုံးပြုသူ အသစ်"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ဧည့်သည်ကို ဖယ်မလား။"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်မလား။"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ဖယ်ထုတ်ပါ"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ပြင်ဆင်သတ်မှတ်ရန်"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ဧည့်သည်ကို ပြန်လည် ကြိုဆိုပါသည်။"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်၏ စက်ရှင်ကို ဆက်လုပ်လိုပါသလား။"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ပြန်စပါ"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ဖန်သားပြင် တစ်စိတ်တစ်ပိုင်းကို ချဲ့ပါ"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ခလုတ်"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"အများသုံးစွဲနိုင်မှုခလုတ်က အများသုံးစွဲနိုင်မှုလက်ဟန်ကို အစားထိုးသည်\n\n"<annotation id="link">"ဆက်တင်များကို ကြည့်ပါ"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"အများသုံးစွဲနိုင်မှု လက်ဟန်မှ ခလုတ်သို့ ပြောင်းနိုင်သည်\n\n"<annotation id="link">"ဆက်တင်များ"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ခလုတ်ကို ယာယီဝှက်ရန် အစွန်းသို့ရွှေ့ပါ"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ဘယ်ဘက်ထိပ်သို့ ရွှေ့ရန်"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ညာဘက်ထိပ်သို့ ရွှေ့ရန်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index c181089321de..71ae67261280 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Legg til brukere"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruker"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gjesten?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle appene og all informasjon i denne økten slettes."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbake, gjest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstørr en del av skjermen"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Bytt"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tilgjengelighet-knappen har erstattet tilgjengelighetsbevegelsen\n\n"<annotation id="link">"Se innstillingene"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Du kan bytte fra tilgjengelighetsbevegelsen til en knapp\n\n"<annotation id="link">"Innstillinger"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytt knappen til kanten for å skjule den midlertidig"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytt til øverst til venstre"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytt til øverst til høyre"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index a69be5d76035..52e615519148 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"प्रयोगकर्ता थप्नुहोस्"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नयाँ प्रयोगकर्ता"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि हटाउने हो?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"हटाउनुहोस्"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"तपाईंलाई फेरि स्वागत छ, अतिथि"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"बदल्नुहोस्"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"एक्सेसिबिलिटी इसाराका स्थानमा एक्सेसिबिलिटी बटन प्रयोग हुन थालेको छ\n\n"<annotation id="link">"सेटिङ हेर्नुहोस्"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"तपाईं एक्सेसिबिलिटी जेस्चरको साटो बटन प्रयोग गर्न सक्नुहुन्छ\n\n"<annotation id="link">"सेटिङ"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"यो बटन केही बेर नदेखिने पार्न किनारातिर सार्नुहोस्"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सिरानको बायाँतिर सार्नुहोस्"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सिरानको दायाँतिर सार्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 8e6293aa2e04..b98694e2fae7 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -21,8 +21,8 @@ It's fine to override this color since at that point the shade was dark. --> <color name="notification_legacy_background_color">@color/GM2_grey_900</color> - <!-- The color of the dividing line between grouped notifications while . --> - <color name="notification_divider_color">#212121</color> + <!-- The color of the dividing line between grouped notifications. --> + <color name="notification_divider_color">@*android:color/background_device_default_dark</color> <!-- The color of the gear shown behind a notification --> <color name="notification_gear_color">@color/GM2_grey_500</color> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index e91f0a034842..8bd41deb9499 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Gebruiker toevoegen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nieuwe gebruiker"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast verwijderen?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Gastsessie resetten?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwijderen"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Resetten"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gast!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Deel van het scherm vergroten"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schakelen"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"De knop Toegankelijkheid vervangt het toegankelijkheidsgebaar\n\n"<annotation id="link">"Instellingen bekijken"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Je kunt overschakelen van het toegankelijkheidsgebaar naar een knop\n\n"<annotation id="link">"Instellingen"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Knop naar de rand verplaatsen om deze tijdelijk te verbergen"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Naar linksboven verplaatsen"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Naar rechtsboven verplaatsen"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 739885d4dbc8..003ce4c4b5ce 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରନ୍ତୁ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ଅତିଥିଙ୍କୁ କାଢ଼ିଦେବେ?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ଅତିଥିଙ୍କୁ ରିସେଟ୍ କରିବେ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହୋଇଯିବ।"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"କାଢ଼ିଦିଅନ୍ତୁ"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ରିସେଟ୍ କରନ୍ତୁ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ପୁଣି ସ୍ୱାଗତ, ଅତିଥି!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ସେସନ୍ ଜାରି ରଖିବାକୁ ଚାହାଁନ୍ତି କି?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string> @@ -1034,6 +1036,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ସ୍କ୍ରିନର ଅଂଶ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ସ୍ୱିଚ୍ କରନ୍ତୁ"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ଆକ୍ସେସିବିଲିଟୀ ଜେଶ୍ଚରକୁ ଆକ୍ସେସିବିଲିଟୀ ବଟନରେ ପରିବର୍ତ୍ତନ କରାଯାଇଛି\n\n"<annotation id="link">"ସେଟିଂସ୍ ଦେଖନ୍ତୁ"</annotation></string> + <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> + <skip /> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ବଟନକୁ ଅସ୍ଥାୟୀ ଭାବେ ଲୁଚାଇବା ପାଇଁ ଏହାକୁ ଗୋଟିଏ ଧାରକୁ ମୁଭ୍ କରନ୍ତୁ"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ଶୀର୍ଷ ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ଶୀର୍ଷ ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index deca3ff30559..0f687a142544 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ਕੀ ਮਹਿਮਾਨ ਹਟਾਉਣਾ ਹੈ?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿੱਚ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ਹਟਾਓ"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ਮਹਿਮਾਨ, ਫਿਰ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string> @@ -742,7 +746,7 @@ <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਵਧਾਇਆ ਗਿਆ"</string> <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਘਟਾਇਆ ਗਿਆ"</string> <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string> - <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string> + <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ, ਜੋ ਕਿ ਬਬਲ ਵਜੋਂ ਦਿਸਦੀਆਂ ਹਨ"</string> <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string> <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ਸੈਟਿੰਗਾਂ"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ਸਵਿੱਚ"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਪਹੁੰਚਯੋਗਤਾ ਸੰਕੇਤ ਨਾਲ ਬਦਲ ਦਿੱਤਾ ਗਿਆ\n\n"<annotation id="link">"ਸੈਟਿੰਗਾਂ ਦੇਖੋ"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ਤੁਸੀਂ ਪਹੁੰਚਯੋਗਤਾ ਇਸ਼ਾਰੇ ਤੋਂ ਬਟਨ \'ਤੇ ਸਵਿੱਚ ਕਰ ਸਕਦੇ ਹੋ\n\n"<annotation id="link">"ਸੈਟਿੰਗਾਂ"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ਬਟਨ ਨੂੰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਲੁਕਾਉਣ ਲਈ ਕਿਨਾਰੇ \'ਤੇ ਲਿਜਾਓ"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ਉੱਪਰ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 217b2f2fa971..e822d936eca4 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -484,8 +484,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodaj użytkownika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nowy użytkownik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Usunąć gościa?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Usuń"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Witaj ponownie, Gościu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string> @@ -1044,6 +1048,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Powiększ część ekranu"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Przełącz"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Przycisk ułatwień dostępu zastąpił gest ułatwień dostępu\n\n"<annotation id="link">"Wyświetl ustawienia"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Możesz przełączyć się z gestu na przycisk ułatwień dostępu.\n\n"<annotation id="link">"Ustawienia"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Przesuń przycisk do krawędzi, aby ukryć go tymczasowo"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Przenieś w lewy górny róg"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Przenieś w prawy górny róg"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 6077c54a8e78..e4dbca5eb8e3 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover visitante?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Redefinir sessão de visitante?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Redefinir"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Você voltou, visitante!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botão de acessibilidade substituiu o gesto de acessibilidade\n\n"<annotation id="link">"Veja as configurações"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Você pode mudar do gesto de acessibilidade para um botão\n\n"<annotation id="link">"Configurações"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 2e7c629b3849..a9cb3dc88fa1 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Adicionar utilizador"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo utilizador"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover o convidado?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Pretende repor a sessão de convidado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Repor"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo de volta, convidado!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte do ecrã"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Mudar"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botão Acessibilidade substituiu o gesto de acessibilidade\n\n"<annotation id="link">"Ver definições"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Pode mudar de um gesto de acessibilidade para um botão\n\n"<annotation id="link">"Definições"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a extremidade para o ocultar temporariamente"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover p/ parte sup. esquerda"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover parte superior direita"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 6077c54a8e78..e4dbca5eb8e3 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover visitante?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Redefinir sessão de visitante?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Redefinir"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Você voltou, visitante!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botão de acessibilidade substituiu o gesto de acessibilidade\n\n"<annotation id="link">"Veja as configurações"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Você pode mudar do gesto de acessibilidade para um botão\n\n"<annotation id="link">"Configurações"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index d6c3756b52c7..3f9a576156d5 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -482,8 +482,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Adăugați un utilizator"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Utilizator nou"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ștergeți invitatul?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ștergeți"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ați revenit în sesiunea pentru invitați!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string> @@ -1039,6 +1043,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Măriți o parte a ecranului"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Comutator"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Butonul de accesibilitate a înlocuit gestul de accesibilitate\n\n"<annotation id="link">"Vedeți setările"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puteți trece de la gestul de accesibilitate la un buton\n\n"<annotation id="link">"Setări"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mutați butonul spre margine pentru a-l ascunde temporar"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mutați în stânga sus"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mutați în dreapta sus"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 33f47de80868..67091b957467 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -484,8 +484,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Добавить пользователя"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новый пользователь"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Удалить аккаунт гостя?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Удалить"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Рады видеть вас снова!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string> @@ -1044,6 +1048,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличить часть экрана"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Переключить"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Жест заменен на кнопку специальных возможностей\n\n"<annotation id="link">"Открыть настройки"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Вы можете использовать кнопку вместо жеста.\n\n"<annotation id="link">"Настройки"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Чтобы временно скрыть кнопку, переместите ее к краю экрана"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перенести в левый верхний угол"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перенести в правый верхний угол"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index cc27dfb7b1b2..6c82ad58a25f 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"පරිශීලකයෙක් එක් කරන්න"</string> <string name="user_new_user_name" msgid="2019166282704195789">"නව පරිශීලකයා"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"අමුත්තාන් ඉවත් කරන්නද?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ආගන්තුකයා යළි සකසන්නද?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ඉවත් කරන්න"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"යළි සකසන්න"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"නැවත සාදරයෙන් පිළිගනිමු, අමුත්තා!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්යද?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"තිරයේ කොටසක් විශාලනය කරන්න"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ස්විචය"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ප්රවේශ්යතා බොත්තම ප්රවේශ්යතා ඉංගිතය ප්රතිස්ථාපනය කළේය\n\n"<annotation id="link">"සැකසීම් බලන්න"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ඔබට ප්රවේශ්යතා ඉංගිතයෙන් බොත්තම් \n\n"<annotation id="link">"සැකසීම්වලට"</annotation>" මාරු විය හැකිය"</string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"එය තාවකාලිකව සැඟවීමට බොත්තම දාරයට ගෙන යන්න"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ඉහළ වමට ගෙන යන්න"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ඉහළ දකුණට ගෙන යන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 0b74313dcf89..19a871e461d3 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -484,8 +484,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Pridať používateľa"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový používateľ"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstrániť hosťa?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrániť"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hosť, vitajte späť!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string> @@ -1044,6 +1048,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zväčšiť časť obrazovky"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prepnúť"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tlačidlo dostupnosti nahradilo gesto dostupnosti\n\n"<annotation id="link">"Zobraziť nastavenia"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Môžete prepnúť z gesta dostupnosti na tlačidlo\n\n"<annotation id="link">"Nastavenia"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ak chcete tlačidlo dočasne skryť, presuňte ho k okraju"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Presunúť doľava nahor"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Presunúť doprava nahor"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index b4729f4ec9f2..c58343a9fd9e 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -484,8 +484,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodajanje uporabnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nov uporabnik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite odstraniti gosta?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrani"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Znova pozdravljeni, gost!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string> @@ -1044,6 +1048,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povečava dela zaslona"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Stikalo"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Gumb za funkcije za ljudi s posebnimi potrebami je zamenjal pripadajočo potezo.\n\n"<annotation id="link">"Ogled nastavitev"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Preklopite lahko s poteze na gumb za funkcije z ljudmi za posebne potrebe.\n\n"<annotation id="link">"Nastavitve"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Če želite gumb začasno skriti, ga premaknite ob rob."</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premakni zgoraj levo"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premakni zgoraj desno"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 3ec14ea3fd81..334b2ed0a603 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Shto përdorues"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Përdorues i ri"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Të hiqet i ftuari?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hiq"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Mirë se erdhe, i ftuar!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ndërro"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Butoni i qasshmërisë zëvendësoi gjestin e qasshmërisë\n\n"<annotation id="link">"Shiko cilësimet"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Mund të kalosh nga gjesti i qasshmërisë te një buton\n\n"<annotation id="link">"Cilësimet"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Zhvendose butonin në skaj për ta fshehur përkohësisht"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Zhvendos lart majtas"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Zhvendos lart djathtas"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 70fcb263ea6b..e4a1bea6f998 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -482,8 +482,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Додај корисника"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нови корисник"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Желите ли да уклоните госта?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Уклони"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добро дошли назад, госте!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string> @@ -1039,6 +1043,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увећајте део екрана"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пређи"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Дугме Приступачност је заменило покрет за приступачност\n\n"<annotation id="link">"Прикажи подешавања"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Можете да пређете са покрета за приступачност на дугме\n\n"<annotation id="link">"Подешавања"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Померите дугме до ивице да бисте га привремено сакрили"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 623337079e3f..390a7bc268e3 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Lägg till användare"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny användare"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vill du ta bort gästen?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Vill du återställa gästsessionen?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ta bort"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Återställ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Välkommen tillbaka som gäst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Förstora en del av skärmen"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Reglage"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tillgänglighetsknappen har ersatt tillgänglighetsrörelsen\n\n"<annotation id="link">"Visa inställningarna"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Du kan byta från tillgänglighetsrörelsen till en knapp\n\n"<annotation id="link">"Inställningar"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytta knappen till kanten för att dölja den tillfälligt"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytta högst upp till vänster"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytta högst upp till höger"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index f7e50effb982..99bf399b3c1b 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Ongeza mtumiaji"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Mtumiaji mpya"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ungependa kumwondoa mgeni?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Ungependa kubadilisha kipindi cha mgeni?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ondoa"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Badilisha"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Karibu tena mgeni!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza upya"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Kuza sehemu ya skrini"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Swichi"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Kitufe cha zana za ufikivu kimechukua nafasi ya ishara ya ufikivu\n\n"<annotation id="link">"Angalia mipangilio"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Unaweza kubadilisha uache kutumia ishara ya ufikivu ili utumie kitufe\n\n"<annotation id="link">"Mipangilio"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sogeza kitufe kwenye ukingo ili ukifiche kwa muda"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sogeza juu kushoto"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sogeza juu kulia"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index df52b0ed9fc1..5dc158195ddc 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"பயனரைச் சேர்"</string> <string name="user_new_user_name" msgid="2019166282704195789">"புதியவர்"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"கெஸ்ட்டை அகற்றவா?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"அகற்று"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"நல்வரவு!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"திரையின் ஒரு பகுதியைப் பெரிதாக்கும்"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ஸ்விட்ச்"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"அணுகல்தன்மை பட்டன் இப்போது அணுகல்தன்மை சைகையாக மாற்றப்பட்டுள்ளது\n\n"<annotation id="link">"அமைப்புகளில் காண்க"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"அணுகல்தன்மை சைகையிலிருந்து பட்டனுக்கு மாறிக்கொள்ளலாம்\n\n"<annotation id="link">"அமைப்புகள்"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"பட்டனைத் தற்காலிகமாக மறைக்க ஓரத்திற்கு நகர்த்தும்"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"மேலே இடதுபுறத்திற்கு நகர்த்து"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"மேலே வலதுபுறத்திற்கு நகர்த்து"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 84b33a9bbf62..348c37ae2e60 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"యూజర్ను జోడించండి"</string> <string name="user_new_user_name" msgid="2019166282704195789">"కొత్త వినియోగదారు"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"గెస్ట్ను తీసివేయాలా?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"తీసివేయి"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"గెస్ట్కు తిరిగి స్వాగతం!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్ని కొనసాగించాలనుకుంటున్నారా?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string> @@ -1034,6 +1038,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"స్క్రీన్లో భాగాన్ని మాగ్నిఫై చేయండి"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"స్విచ్ చేయి"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"యాక్సెసిబిలిటీ బటన్, యాక్సెసిబిలిటీ సంజ్ఞను భర్తీ చేసింది\n\n"<annotation id="link">"సెట్టింగ్లను చూడండి"</annotation></string> + <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> + <skip /> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"తాత్కాలికంగా దానిని దాచడానికి బటన్ను చివరకు తరలించండి"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ఎగువ ఎడమ వైపునకు తరలించు"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ఎగువ కుడి వైపునకు తరలించు"</string> diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 70bd85036198..2f0957caaaae 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -33,6 +33,7 @@ <item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item> <item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item> <item>com.android.systemui.statusbar.tv.VpnStatusObserver</item> + <item>com.android.systemui.globalactions.GlobalActionsComponent</item> <item>com.android.systemui.usb.StorageNotification</item> <item>com.android.systemui.power.PowerUI</item> <item>com.android.systemui.media.RingtonePlayer</item> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 2b7bc5812b2a..b21d22faf958 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"เพิ่มผู้ใช้"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ผู้ใช้ใหม่"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ต้องการนำผู้ใช้ชั่วคราวออกไหม"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"รีเซ็ตผู้เข้าร่วมไหม"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"นำออก"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"รีเซ็ต"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ยินดีต้อนรับผู้เข้าร่วมกลับมาอีกครั้ง"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"เปลี่ยน"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ปุ่มการช่วยเหลือพิเศษแทนที่ท่าทางสัมผัสการช่วยเหลือพิเศษแล้ว\n\n"<annotation id="link">"ดูการตั้งค่า"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"คุณสลับจากท่าทางสัมผัสเพื่อการเข้าถึงพิเศษไปที่ปุ่ม\n\n"<annotation id="link">"การตั้งค่า"</annotation>"ได้"</string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ย้ายปุ่มไปที่ขอบเพื่อซ่อนชั่วคราว"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ย้ายไปด้านซ้ายบน"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ย้ายไปด้านขวาบน"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 8db4e9e76f2a..5b48488f18fe 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Magdagdag ng user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Bagong user"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alisin ang bisita?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"I-reset ang session ng bisita?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alisin"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"I-reset"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome ulit, bisita!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"I-magnify ang isang bahagi ng screen"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Pinalitan ng button ng accessibility ang galaw ng accessibility\n\n"<annotation id="link">"Tingnan ang mga setting"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puwede kang lumipat sa button mula sa galaw para sa accessibility\n\n"<annotation id="link">"Mga Setting"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ilipat ang button sa gilid para pansamantala itong itago"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Ilipat sa kaliwa sa itaas"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Ilipat sa kanan sa itaas"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 568be8a5a7d7..8a3d467fbe58 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Kullanıcı ekle"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni kullanıcı"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Misafir oturumu kaldırılsın mı?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Misafir oturumu sıfırlansın mı?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kaldır"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Sıfırla"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Misafir kullanıcı, tekrar hoşgeldiniz"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir parçasını büyütün"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Geç"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Erişilebilirlik hareketi, Erişilebilirlik düğmesi ile değiştirildi\n\n"<annotation id="link">"Ayarları görüntüle"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Erişilebilirlik hareketi yerine bir düğme kullanmaya geçebilirsiniz\n\n"<annotation id="link">"Ayarlar"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düğmeyi geçici olarak gizlemek için kenara taşıyın"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sol üste taşı"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sağ üste taşı"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 7aef2526ab16..89be1299e836 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -484,8 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Додати користувача"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новий користувач"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Видалити гостя?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Скинути сеанс у режимі \"Гість\"?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Вийти"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Скинути"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"З поверненням!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продовжити сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почати знову"</string> @@ -1044,6 +1046,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Збільшити частину екрана"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Перемкнути"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Замість жесту спеціальних можливостей тепер використовується кнопка\n\n"<annotation id="link">"Переглянути налаштування"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Ви можете використовувати кнопку замість жесту спеціальних можливостей\n\n"<annotation id="link">"Налаштування"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Щоб тимчасово сховати кнопку, перемістіть її на край екрана"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перемістити ліворуч угору"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перемістити праворуч угору"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index b8911fda4f7f..930bb0586753 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"صارف کو شامل کریں"</string> <string name="user_new_user_name" msgid="2019166282704195789">"نیا صارف"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مہمان کو ہٹائیں؟"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ہٹائیں"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مہمان، پھر سے خوش آمدید!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"سوئچ کریں"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ایکسیسبیلٹی بٹن کو ایکسیسبیلٹی اشارے سے بدل دیا گیا\n\n"<annotation id="link">"ترتیبات دیکھیں"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"آپ ایکسیسبیلٹی اشارے سے بٹن پر سوئچ کر سکتے ہیں\n\n"<annotation id="link">"ترتیبات"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"عارضی طور پر بٹن کو چھپانے کے لئے اسے کنارے پر لے جائیں"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"اوپر بائیں جانب لے جائیں"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"اوپر دائیں جانب لے جائيں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index f41193887e61..df7f7cd72ee9 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Foydalanuvchi"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yangi foydalanuvchi"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Mehmon olib tashlansinmi?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Mehmon seansi tiklansinmi?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Olib tashlash"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Tiklash"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xush kelibsiz, mehmon!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Seansni davom ettirmoqchimisiz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Boshidan boshlansin"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Almashtirish"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Maxsus imkoniyatlar tugmasi maxsus imkoniyatlar ishorasini almashtirdi\n\n"<annotation id="link">"Sozlamalarni ochish"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Siz maxsus imkoniyatlar ishorasi oʻrniga \n\n"<annotation id="link">"Sozlamalar"</annotation>" tugmasidan foydalanishingiz mumkin"</string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Vaqtinchalik berkitish uchun tugmani qirra tomon suring"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuqori chapga surish"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuqori oʻngga surish"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 1e10496573f1..241cfb222a18 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Thêm người dùng"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Người dùng mới"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Xóa phiên khách?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Xóa"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Chào mừng bạn trở lại!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Phóng to một phần màn hình"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Chuyển"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Nút hỗ trợ tiếp cận đã thay thế cử chỉ hỗ trợ tiếp cận\n\n"<annotation id="link">"Xem chế độ cài đặt"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Bạn có thể chuyển từ cử chỉ hỗ trợ tiếp cận sang nút hỗ trợ tiếp cận\n\n"<annotation id="link">"Cài đặt"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Di chuyển nút sang cạnh để ẩn nút tạm thời"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Chuyển lên trên cùng bên trái"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Chuyển lên trên cùng bên phải"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 9272b8adb2c4..9592b2d6aa5c 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"添加用户"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新用户"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"要移除访客吗?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"要重置访客会话吗?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"重置"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"访客,欢迎回来!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分屏幕"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切换"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"“无障碍”按钮已取代无障碍手势\n\n"<annotation id="link">"查看设置"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"您可以从使用无障碍手势改为使用按钮\n\n"<annotation id="link">"设置"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"将按钮移到边缘,即可暂时将其隐藏"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移至左上角"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移至右上角"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index fff99e4b876f..7be08d3fda5a 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -480,8 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"加入使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"要重設訪客嗎?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"重設"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客您好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> @@ -1034,6 +1036,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分螢幕畫面"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"無障礙功能按鈕已取代無障礙手勢\n\n"<annotation id="link">"查看設定"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"您可以從無障礙手勢改為使用按鈕\n\n"<annotation id="link">"設定"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣即可暫時隱藏"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移去左上方"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移去右上方"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 9a278fd1a0dc..5c4c3b15e997 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"新增使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會遭到刪除。"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客你好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大局部螢幕畫面"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"無障礙工具按鈕已取代無障礙手勢\n\n"<annotation id="link">"查看設定"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"你可以從無障礙手勢改為使用按鈕\n\n"<annotation id="link">"設定"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣處即可暫時隱藏"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移到左上方"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移到右上方"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index a11c5a82a9a0..647102ac4895 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -480,8 +480,12 @@ <string name="user_add_user" msgid="4336657383006913022">"Engeza umsebenzisi"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Umsebenzisi omusha"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Susa isivakashi?"</string> + <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Zonke izinhlelo zokusebenza nedatha kulesi sikhathi zizosuswa."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Susa"</string> + <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Siyakwamukela futhi, sivakashi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string> @@ -1034,6 +1038,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Khulisa ingxenye eyesikrini"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Iswishi"</string> <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Inkinobho yokufinyeleleka ishintshaniswe ngokuthinta kokufinyeleleka\n\n"<annotation id="link">"Buka amasethingi"</annotation></string> + <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Ungashintsha kusuka kunzwa yokufinyeleleka ukuya kunkinobho\n\n"<annotation id="link">"Amasethingi"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Hambisa inkinobho onqenqemeni ukuze uyifihle okwesikhashana"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Hamba phezulu kwesokunxele"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Hamba phezulu ngakwesokudla"</string> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 067d56f3d157..d2ed6017b205 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -143,6 +143,7 @@ <attr name="handleThickness" format="dimension" /> <attr name="handleColor" format="color" /> <attr name="scrimColor" format="color" /> + <attr name="containerBackgroundColor" format="color" /> <attr name="isVertical" format="boolean" /> @@ -178,6 +179,7 @@ <attr name="handleThickness" /> <attr name="handleColor" /> <attr name="scrimColor" /> + <attr name="containerBackgroundColor" /> </declare-styleable> <declare-styleable name="MagnifierView"> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 2cf30581326b..e7edb0e6a57d 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -93,7 +93,7 @@ <color name="notification_material_background_dark_color">#ff333333</color> <!-- The color of the dividing line between grouped notifications. --> - <color name="notification_divider_color">#FF616161</color> + <color name="notification_divider_color">@*android:color/background_device_default_light</color> <!-- The color of the ripples on the untinted notifications --> <color name="notification_ripple_untinted_color">#28000000</color> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index b4deaa0af543..2a1bee5344ec 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -396,10 +396,6 @@ <!-- Whether or not the notifications should always fade as they are dismissed. --> <bool name="config_fadeNotificationsOnDismiss">false</bool> - <!-- Whether or not the parent of the notification row itself is being translated when swiped or - its children views. If true, then the contents are translated and vice versa. --> - <bool name="config_translateNotificationContentsOnSwipe">true</bool> - <!-- Whether or not the fade on the notification is based on the amount that it has been swiped off-screen. --> <bool name="config_fadeDependingOnAmountSwiped">false</bool> @@ -430,7 +426,7 @@ <!-- Whether or not the dividing lines should be shown when the container is expanding and collapsing. If this value is true, then the lines will only show when the container has been completely expanded. --> - <bool name="config_hideDividersDuringExpand">false</bool> + <bool name="config_hideDividersDuringExpand">true</bool> <!-- Whether or not child notifications that are part of a group will have shadows. --> <bool name="config_enableShadowOnChildNotifications">true</bool> @@ -449,6 +445,9 @@ <!-- Adjust the theme on fully custom and decorated custom view notifications --> <bool name="config_adjustThemeOnNotificationCustomViews">false</bool> + <!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. --> + <bool name="config_skinnyNotifsInLandscape">true</bool> + <!-- If true, enable the advance anti-falsing classifier on the lockscreen. On some devices it does not work well, particularly with noisy touchscreens. Note that disabling it may increase the rate of unintentional unlocks. --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0ccde60f9bfd..3e4684c9b2d6 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -347,7 +347,7 @@ <!-- Padding to make tappable chip height 48dp (18+11+11+4+4) --> <dimen name="screenshot_action_chip_margin_vertical">4dp</dimen> <dimen name="screenshot_action_chip_padding_vertical">11dp</dimen> - <dimen name="screenshot_action_chip_icon_size">18dp</dimen> + <dimen name="screenshot_action_chip_icon_size">18sp</dimen> <!-- Padding on each side of the icon for icon-only chips --> <dimen name="screenshot_action_chip_icon_only_padding_horizontal">14dp</dimen> <!-- Padding at the edges of the chip for icon-and-text chips --> @@ -685,7 +685,7 @@ <dimen name="notification_shadow_radius">0dp</dimen> <!-- The alpha of the dividing line between child notifications of a notification group. --> - <item name="notification_divider_alpha" format="float" type="dimen">0.5</item> + <item name="notification_divider_alpha" format="float" type="dimen">1</item> <!-- The height of the divider between the individual notifications in a notification group. --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7f4e4751312e..83dbad1677aa 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1148,12 +1148,18 @@ <!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] --> <string name="guest_exit_guest_dialog_title">Remove guest?</string> + <!-- Title of the confirmation dialog when resetting guest session [CHAR LIMIT=NONE] --> + <string name="guest_reset_guest_dialog_title">Reset guest?</string> + <!-- Message of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] --> <string name="guest_exit_guest_dialog_message">All apps and data in this session will be deleted.</string> <!-- Label for button in confirmation dialog when exiting guest session [CHAR LIMIT=35] --> <string name="guest_exit_guest_dialog_remove">Remove</string> + <!-- Label for button in confirmation dialog when resetting guest session [CHAR LIMIT=35] --> + <string name="guest_reset_guest_dialog_remove">Reset</string> + <!-- Title of the notification when resuming an existing guest session [CHAR LIMIT=NONE] --> <string name="guest_wipe_session_title">Welcome back, guest!</string> @@ -2705,6 +2711,8 @@ <!-- Accessibility floating menu strings --> <!-- Message for the accessibility floating button migration tooltip. It shows when the user use gestural navigation then upgrade their system. It will tell the user the accessibility gesture had been replaced by accessibility floating button. [CHAR LIMIT=100] --> <string name="accessibility_floating_button_migration_tooltip">Accessibility button replaced the accessibility gesture\n\n<annotation id="link">View settings</annotation></string> + <!-- Message for the accessibility floating button settings tooltip. It shows when the user use gestural navigation then upgrade their system. It will tell the user to have another option to switch from the accessibility gesture to a button. [CHAR LIMIT=100] --> + <string name="accessibility_floating_button_switch_migration_tooltip">You can switch from the accessibility gesture to a button\n\n<annotation id="link">Settings</annotation></string> <!-- Message for the accessibility floating button docking tooltip. It shows when the user first time drag the button. It will tell the user about docking behavior. [CHAR LIMIT=70] --> <string name="accessibility_floating_button_docking_tooltip">Move button to the edge to hide it temporarily</string> <!-- Action in accessibility menu to move the accessibility floating button to the top left of the screen. [CHAR LIMIT=30] --> @@ -2977,6 +2985,8 @@ <!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] --> <string name="ongoing_phone_call_content_description">Ongoing phone call</string> + <!-- Placeholder for string describing changes in global actions --> + <string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string> <!-- URL for more information about changes in global actions --> <string name="global_actions_change_url" translatable="false"></string> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 97273a88e9c0..7a5a3480d0c5 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -883,5 +883,6 @@ <style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault"> <item name="android:colorBackground">@android:color/system_neutral1_900</item> + <item name="android:itemBackground">@android:color/system_neutral1_800</item> </style> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java index 42d2333587b4..442716878af4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java @@ -17,6 +17,7 @@ package com.android.systemui.shared.system; import android.annotation.IntDef; +import android.os.Build; import android.view.View; import com.android.internal.jank.InteractionJankMonitor; @@ -58,23 +59,48 @@ public final class InteractionJankMonitorWrapper { public @interface CujType { } - public static boolean begin(View v, @CujType int cujType) { - return InteractionJankMonitor.getInstance().begin(v, cujType); + /** + * Begin a trace session. + * + * @param v an attached view. + * @param cujType the specific {@link InteractionJankMonitor.CujType}. + */ + public static void begin(View v, @CujType int cujType) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; + InteractionJankMonitor.getInstance().begin(v, cujType); } - public static boolean begin(View v, @CujType int cujType, long timeout) { + /** + * Begin a trace session. + * + * @param v an attached view. + * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param timeout duration to cancel the instrumentation in ms + */ + public static void begin(View v, @CujType int cujType, long timeout) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; Configuration.Builder builder = new Configuration.Builder(cujType) .setView(v) .setTimeout(timeout); - return InteractionJankMonitor.getInstance().begin(builder); + InteractionJankMonitor.getInstance().begin(builder); } - public static boolean end(@CujType int cujType) { - return InteractionJankMonitor.getInstance().end(cujType); + /** + * End a trace session. + * + * @param cujType the specific {@link InteractionJankMonitor.CujType}. + */ + public static void end(@CujType int cujType) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; + InteractionJankMonitor.getInstance().end(cujType); } - public static boolean cancel(@CujType int cujType) { - return InteractionJankMonitor.getInstance().cancel(cujType); + /** + * Cancel the trace session. + */ + public static void cancel(@CujType int cujType) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; + InteractionJankMonitor.getInstance().cancel(cujType); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index f3a6d6377c6d..38f8f7ac321f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -249,6 +249,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab public void onStateChanged(int newState) { mStatusBarState = newState; } + + @Override + public void onExpandedChanged(boolean isExpanded) { + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onShadeExpandedChanged(isExpanded); + } + } + } }; HashMap<Integer, SimData> mSimDatas = new HashMap<>(); @@ -775,6 +785,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) { mFingerprintLockedOut = true; + if (isUdfpsEnrolled()) { + updateFingerprintListeningState(); + } } for (int i = 0; i < mCallbacks.size(); i++) { @@ -2115,7 +2128,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || (!getUserCanSkipBouncer(getCurrentUser()) && !isEncryptedOrLockdown(getCurrentUser()) && !userNeedsStrongAuth() - && userDoesNotHaveTrust); + && userDoesNotHaveTrust + && !mFingerprintLockedOut); return shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState && shouldListenUdfpsState; } @@ -3244,6 +3258,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags)); pw.println(" trustManaged=" + getUserTrustIsManaged(userId)); pw.println(" udfpsEnrolled=" + isUdfpsEnrolled()); + pw.println(" mFingerprintLockedOut=" + mFingerprintLockedOut); pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId)); if (isUdfpsEnrolled()) { pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true)); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index e561a5a84f24..9849a7efe837 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -333,4 +333,8 @@ public class KeyguardUpdateMonitorCallback { */ public void onRequireUnlockForNfc() { } + /** + * Called when the notification shade is expanded or collapsed. + */ + public void onShadeExpandedChanged(boolean expanded) { } } diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java index 45a0ea19c8dc..0ae89bc10290 100644 --- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java @@ -27,15 +27,14 @@ import android.content.IntentFilter; import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserHandle; -import android.os.UserManager; import android.provider.Settings; import android.util.Log; -import android.view.WindowManagerGlobal; import com.android.internal.logging.UiEventLogger; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.policy.UserSwitcherController; /** * Manages notification when a guest session is resumed. @@ -47,9 +46,12 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in"; private Dialog mNewSessionDialog; + private final UserSwitcherController mUserSwitcherController; private final UiEventLogger mUiEventLogger; - public GuestResumeSessionReceiver(UiEventLogger uiEventLogger) { + public GuestResumeSessionReceiver(UserSwitcherController userSwitcherController, + UiEventLogger uiEventLogger) { + mUserSwitcherController = userSwitcherController; mUiEventLogger = uiEventLogger; } @@ -90,7 +92,8 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { int notFirstLogin = Settings.System.getIntForUser( cr, SETTING_GUEST_HAS_LOGGED_IN, 0, userId); if (notFirstLogin != 0) { - mNewSessionDialog = new ResetSessionDialog(context, mUiEventLogger, userId); + mNewSessionDialog = new ResetSessionDialog(context, mUserSwitcherController, + mUiEventLogger, userId); mNewSessionDialog.show(); } else { Settings.System.putIntForUser( @@ -99,54 +102,6 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { } } - /** - * Wipes the guest session. - * - * The guest must be the current user and its id must be {@param userId}. - */ - private static void wipeGuestSession(Context context, int userId) { - UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - UserInfo currentUser; - try { - currentUser = ActivityManager.getService().getCurrentUser(); - } catch (RemoteException e) { - Log.e(TAG, "Couldn't wipe session because ActivityManager is dead"); - return; - } - if (currentUser.id != userId) { - Log.w(TAG, "User requesting to start a new session (" + userId + ")" - + " is not current user (" + currentUser.id + ")"); - return; - } - if (!currentUser.isGuest()) { - Log.w(TAG, "User requesting to start a new session (" + userId + ")" - + " is not a guest"); - return; - } - - boolean marked = userManager.markGuestForDeletion(currentUser.id); - if (!marked) { - Log.w(TAG, "Couldn't mark the guest for deletion for user " + userId); - return; - } - UserInfo newGuest = userManager.createGuest(context, currentUser.name); - - try { - if (newGuest == null) { - Log.e(TAG, "Could not create new guest, switching back to system user"); - ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM); - userManager.removeUser(currentUser.id); - WindowManagerGlobal.getWindowManagerService().lockNow(null /* options */); - return; - } - ActivityManager.getService().switchUser(newGuest.id); - userManager.removeUser(currentUser.id); - } catch (RemoteException e) { - Log.e(TAG, "Couldn't wipe session because ActivityManager or WindowManager is dead"); - return; - } - } - private void cancelDialog() { if (mNewSessionDialog != null && mNewSessionDialog.isShowing()) { mNewSessionDialog.cancel(); @@ -160,10 +115,12 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { private static final int BUTTON_WIPE = BUTTON_NEGATIVE; private static final int BUTTON_DONTWIPE = BUTTON_POSITIVE; + private final UserSwitcherController mUserSwitcherController; private final UiEventLogger mUiEventLogger; private final int mUserId; - ResetSessionDialog(Context context, UiEventLogger uiEventLogger, int userId) { + ResetSessionDialog(Context context, UserSwitcherController userSwitcherController, + UiEventLogger uiEventLogger, int userId) { super(context); setTitle(context.getString(R.string.guest_wipe_session_title)); @@ -175,6 +132,7 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { setButton(BUTTON_DONTWIPE, context.getString(R.string.guest_wipe_session_dontwipe), this); + mUserSwitcherController = userSwitcherController; mUiEventLogger = uiEventLogger; mUserId = userId; } @@ -183,7 +141,7 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { public void onClick(DialogInterface dialog, int which) { if (which == BUTTON_WIPE) { mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_WIPE); - wipeGuestSession(getContext(), mUserId); + mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL); dismiss(); } else if (which == BUTTON_DONTWIPE) { mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE); diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 64a683e78953..a68f79604b25 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -130,10 +130,7 @@ public class ImageWallpaper extends WallpaperService { .getBounds(); mHeight = window.height(); mWidth = window.width(); - mMiniBitmap = null; - if (mWorker != null && mWorker.getThreadHandler() != null) { - mWorker.getThreadHandler().post(this::updateMiniBitmap); - } + mRenderer.setOnBitmapChanged(this::updateMiniBitmap); } EglHelper getEglHelperInstance() { @@ -177,20 +174,19 @@ public class ImageWallpaper extends WallpaperService { mPageOffset = (1 - imgWidth) / (float) (mPages - 1); } - private void updateMiniBitmap() { - mRenderer.useBitmap(b -> { - int size = Math.min(b.getWidth(), b.getHeight()); - float scale = 1.0f; - if (size > MIN_SURFACE_WIDTH) { - scale = (float) MIN_SURFACE_WIDTH / (float) size; - } - mImgHeight = b.getHeight(); - mImgWidth = b.getWidth(); - mMiniBitmap = Bitmap.createScaledBitmap(b, (int) Math.max(scale * b.getWidth(), 1), - (int) Math.max(scale * b.getHeight(), 1), false); - computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap); - mLocalColorsToAdd.clear(); - }); + private void updateMiniBitmap(Bitmap b) { + if (b == null) return; + int size = Math.min(b.getWidth(), b.getHeight()); + float scale = 1.0f; + if (size > MIN_SURFACE_WIDTH) { + scale = (float) MIN_SURFACE_WIDTH / (float) size; + } + mImgHeight = b.getHeight(); + mImgWidth = b.getWidth(); + mMiniBitmap = Bitmap.createScaledBitmap(b, (int) Math.max(scale * b.getWidth(), 1), + (int) Math.max(scale * b.getHeight(), 1), false); + computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap); + mLocalColorsToAdd.clear(); } private void updateSurfaceSize() { diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index b0f4da251208..affad7a57d86 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -391,9 +391,9 @@ public class SwipeHelper implements Gefingerpoken { boolean animateLeft = (Math.abs(velocity) > getEscapeVelocity() && velocity < 0) || (getTranslation(animView) < 0 && !isDismissAll); if (animateLeft || animateLeftForRtl || animateUpForMenu) { - newPos = -getSize(animView); + newPos = -getTotalTranslationLength(animView); } else { - newPos = getSize(animView); + newPos = getTotalTranslationLength(animView); } long duration; if (fixedDuration == 0) { @@ -470,6 +470,15 @@ public class SwipeHelper implements Gefingerpoken { } /** + * Get the total translation length where we want to swipe to when dismissing the view. By + * default this is the size of the view, but can also be larger. + * @param animView the view to ask about + */ + protected float getTotalTranslationLength(View animView) { + return getSize(animView); + } + + /** * Called to update the dismiss animation. */ protected void prepareDismissAnimation(View view, Animator anim) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java index bfb7105f5860..17178fa8e606 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java @@ -75,7 +75,6 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE; private final LayoutParams mParams; - private int mWindowHeight; @VisibleForTesting final Rect mDraggableWindowBounds = new Rect(); private boolean mIsVisible = false; @@ -95,7 +94,6 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL mWindowManager = mContext.getSystemService(WindowManager.class); mSfVsyncFrameProvider = sfVsyncFrameProvider; mParams = createLayoutParams(context); - mWindowHeight = mWindowManager.getCurrentWindowMetrics().getBounds().height(); mImageView = imageView; mImageView.setOnTouchListener(this::onTouch); mImageView.setAccessibilityDelegate(new View.AccessibilityDelegate() { @@ -313,12 +311,14 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL void onConfigurationChanged(int configDiff) { if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) { + final Rect previousDraggableBounds = new Rect(mDraggableWindowBounds); mDraggableWindowBounds.set(getDraggableWindowBounds()); - // Keep the Y position with the same height ratio before the window height is changed. - final int windowHeight = mWindowManager.getCurrentWindowMetrics().getBounds().height(); - final float windowHeightFraction = (float) mParams.y / mWindowHeight; - mParams.y = (int) (windowHeight * windowHeightFraction); - mWindowHeight = windowHeight; + // Keep the Y position with the same height ratio before the window bounds and + // draggable bounds are changed. + final float windowHeightFraction = (float) (mParams.y - previousDraggableBounds.top) + / previousDraggableBounds.height(); + mParams.y = (int) (windowHeightFraction * mDraggableWindowBounds.height()) + + mDraggableWindowBounds.top; stickToScreenEdge(mToLeftScreenEdge); return; } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java index 47f373920b90..05256e646948 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java @@ -53,7 +53,7 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu { @FloatRange(from = 0.0, to = 1.0) private static final float DEFAULT_POSITION_X_PERCENT = 1.0f; @FloatRange(from = 0.0, to = 1.0) - private static final float DEFAULT_POSITION_Y_PERCENT = 0.8f; + private static final float DEFAULT_POSITION_Y_PERCENT = 0.9f; private final Context mContext; private final AccessibilityFloatingMenuView mMenuView; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java index 63cfd5123c96..ee09c620ec1d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java @@ -143,7 +143,7 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> { } void updateItemPadding(int padding, int size) { - itemView.setPaddingRelative(padding, padding, padding, padding); + itemView.setPaddingRelative(padding, padding, padding, 0); } } @@ -154,7 +154,7 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> { @Override void updateItemPadding(int padding, int size) { - final int paddingBottom = size <= 2 ? padding : 0; + final int paddingBottom = size <= 1 ? padding : 0; itemView.setPaddingRelative(padding, padding, padding, paddingBottom); } } @@ -166,7 +166,7 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> { @Override void updateItemPadding(int padding, int size) { - itemView.setPaddingRelative(padding, 0, padding, padding); + itemView.setPaddingRelative(padding, padding, padding, padding); } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index cf577a37d625..77cca2e3089c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -24,9 +24,13 @@ import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.settingslib.Utils +import com.android.systemui.statusbar.CircleReveal +import com.android.systemui.statusbar.LiftReveal +import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.StatusBar import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope @@ -49,10 +53,12 @@ class AuthRippleController @Inject constructor( private val commandRegistry: CommandRegistry, private val notificationShadeWindowController: NotificationShadeWindowController, private val bypassController: KeyguardBypassController, + private val biometricUnlockController: BiometricUnlockController, rippleView: AuthRippleView? ) : ViewController<AuthRippleView>(rippleView) { var fingerprintSensorLocation: PointF? = null private var faceSensorLocation: PointF? = null + private var circleReveal: LightRevealEffect? = null @VisibleForTesting public override fun onViewAttached() { @@ -96,15 +102,47 @@ class AuthRippleController @Inject constructor( private fun showRipple() { notificationShadeWindowController.setForcePluginOpen(true, this) - mView.startRipple(Runnable { - notificationShadeWindowController.setForcePluginOpen(false, this) - }) + val biometricUnlockMode = biometricUnlockController.mode + val useCircleReveal = circleReveal != null && + (biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK || + biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING || + biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM) + val lightRevealScrim = statusBar.lightRevealScrim + if (useCircleReveal) { + lightRevealScrim?.revealEffect = circleReveal!! + } + + mView.startRipple( + /* end runnable */ + Runnable { + notificationShadeWindowController.setForcePluginOpen(false, this) + if (useCircleReveal) { + lightRevealScrim?.revealEffect = LiftReveal + } + }, + /* circleReveal */ + if (useCircleReveal) { + lightRevealScrim + } else { + null + } + ) } fun updateSensorLocation() { fingerprintSensorLocation = authController.fingerprintSensorLocation faceSensorLocation = authController.faceAuthSensorLocation - statusBar.updateCircleReveal() + fingerprintSensorLocation?.let { + circleReveal = CircleReveal( + it.x, + it.y, + 0f, + Math.max( + Math.max(it.x, statusBar.displayWidth - it.x), + Math.max(it.y, statusBar.displayHeight - it.y) + ) + ) + } } private fun updateRippleColor() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt index 75373abc5124..dd73c4f8d071 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt @@ -31,6 +31,7 @@ import android.util.MathUtils import android.view.View import android.view.animation.PathInterpolator import com.android.internal.graphics.ColorUtils +import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.charging.RippleShader private const val RIPPLE_ANIMATION_DURATION: Long = 1533 @@ -70,51 +71,79 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at .toFloat() } - fun startRipple(onAnimationEnd: Runnable?) { + fun startRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) { if (rippleInProgress) { return // Ignore if ripple effect is already playing } - val animator = ValueAnimator.ofFloat(0f, 1f) - animator.interpolator = PathInterpolator(0.4f, 0f, 0f, 1f) - animator.duration = RIPPLE_ANIMATION_DURATION - animator.addUpdateListener { animator -> - val now = animator.currentPlayTime - rippleShader.progress = animator.animatedValue as Float - rippleShader.time = now.toFloat() - rippleShader.distortionStrength = 1 - rippleShader.progress - invalidate() + val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply { + interpolator = PathInterpolator(0.4f, 0f, 0f, 1f) + duration = RIPPLE_ANIMATION_DURATION + addUpdateListener { animator -> + val now = animator.currentPlayTime + rippleShader.progress = animator.animatedValue as Float + rippleShader.time = now.toFloat() + + lightReveal?.revealAmount = animator.animatedValue as Float + invalidate() + } } - val alphaInAnimator = ValueAnimator.ofInt(0, 127) - alphaInAnimator.duration = 167 - alphaInAnimator.addUpdateListener { alphaInAnimator -> - rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color, - alphaInAnimator.animatedValue as Int) - invalidate() + + val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply { + interpolator = rippleAnimator.interpolator + startDelay = 10 + duration = rippleAnimator.duration + addUpdateListener { animator -> + lightReveal?.revealAmount = animator.animatedValue as Float + } } - val alphaOutAnimator = ValueAnimator.ofInt(127, 0) - alphaOutAnimator.startDelay = 417 - alphaOutAnimator.duration = 1116 - alphaOutAnimator.addUpdateListener { alphaOutAnimator -> - rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color, - alphaOutAnimator.animatedValue as Int) - invalidate() + + val alphaInAnimator = ValueAnimator.ofInt(0, 127).apply { + duration = 167 + addUpdateListener { animator -> + rippleShader.color = ColorUtils.setAlphaComponent( + rippleShader.color, + animator.animatedValue as Int + ) + invalidate() + } } - val animatorSet = AnimatorSet() - animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator) - animatorSet.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator?) { - onAnimationEnd?.run() - rippleInProgress = false - visibility = GONE + val alphaOutAnimator = ValueAnimator.ofInt(127, 0).apply { + startDelay = 417 + duration = 1116 + addUpdateListener { animator -> + rippleShader.color = ColorUtils.setAlphaComponent( + rippleShader.color, + animator.animatedValue as Int + ) + invalidate() } - }) + } + + val animatorSet = AnimatorSet().apply { + playTogether( + rippleAnimator, + revealAnimator, + alphaInAnimator, + alphaOutAnimator + ) + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator?) { + rippleInProgress = true + visibility = VISIBLE + } + + override fun onAnimationEnd(animation: Animator?) { + onAnimationEnd?.run() + rippleInProgress = false + visibility = GONE + } + }) + } // TODO (b/185124905): custom haptic TBD // vibrate() animatorSet.start() - visibility = VISIBLE - rippleInProgress = true } fun setColor(color: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index f975a804ca80..ec930b0c41d1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -378,6 +378,7 @@ public class UdfpsController implements DozeReceiver { return true; case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_HOVER_ENTER: + Trace.beginSection("UdfpsController.onTouch.ACTION_DOWN"); // To simplify the lifecycle of the velocity tracker, make sure it's never null // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP. if (mVelocityTracker == null) { @@ -388,8 +389,7 @@ public class UdfpsController implements DozeReceiver { mVelocityTracker.clear(); } if (isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView)) { - Trace.beginAsyncSection( - "UdfpsController#ACTION_DOWN", 1); + Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0); // The pointer that causes ACTION_DOWN is always at index 0. // We need to persist its ID to track it during ACTION_MOVE that could include // data for many other pointers because of multi-touch support. @@ -397,10 +397,12 @@ public class UdfpsController implements DozeReceiver { mVelocityTracker.addMovement(event); handled = true; } + Trace.endSection(); break; case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_HOVER_MOVE: + Trace.beginSection("UdfpsController.onTouch.ACTION_MOVE"); final int idx = mActivePointerId == -1 ? event.getPointerId(0) : event.findPointerIndex(mActivePointerId); @@ -466,11 +468,13 @@ public class UdfpsController implements DozeReceiver { onFingerUp(); } } + Trace.endSection(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_HOVER_EXIT: + Trace.beginSection("UdfpsController.onTouch.ACTION_UP"); mActivePointerId = -1; if (mVelocityTracker != null) { mVelocityTracker.recycle(); @@ -479,7 +483,7 @@ public class UdfpsController implements DozeReceiver { Log.v(TAG, "onTouch | finger up"); onFingerUp(); mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION); - + Trace.endSection(); break; default: @@ -818,12 +822,11 @@ public class UdfpsController implements DozeReceiver { return; } mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); - Trace.endAsyncSection( - "UdfpsController#ACTION_DOWN", 1); - Trace.beginAsyncSection("UdfpsController#startIllumination", 1); + Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0); + Trace.beginAsyncSection("UdfpsController.e2e.startIllumination", 0); mView.startIllumination(() -> { mFingerprintManager.onUiReady(mSensorProps.sensorId); - Trace.endAsyncSection("UdfpsController#startIllumination", 1); + Trace.endAsyncSection("UdfpsController.e2e.startIllumination", 0); }); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java index 6a6f57a64be6..f8be35ab6cd8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java @@ -55,6 +55,7 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { @NonNull private final SurfaceHolder mHolder; @NonNull private final Paint mSensorPaint; @NonNull private final SimpleDrawable mIlluminationDotDrawable; + private final int mOnIlluminatedDelayMs; private final @HbmType int mHbmType; @NonNull private RectF mSensorRect; @@ -82,6 +83,9 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { canvas.drawOval(mSensorRect, mSensorPaint); }; + mOnIlluminatedDelayMs = mContext.getResources().getInteger( + com.android.internal.R.integer.config_udfps_illumination_transition_ms); + if (Build.IS_ENG || Build.IS_USERDEBUG) { mHbmType = Settings.Secure.getIntForUser(mContext.getContentResolver(), SETTING_HBM_TYPE, DEFAULT_HBM_TYPE, UserHandle.USER_CURRENT); @@ -107,9 +111,8 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { } if (onIlluminatedRunnable != null) { // No framework API can reliably tell when a frame reaches the panel. A timeout - // is the safest solution. The frame should be displayed within 3 refresh - // cycles, which on a 60 Hz panel equates to 50 milliseconds. - postDelayed(onIlluminatedRunnable, 50 /* delayMillis */); + // is the safest solution. + postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs); } else { Log.w(TAG, "startIllumination | onIlluminatedRunnable is null"); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index ff95604088ed..455f3c0d6ac2 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -38,6 +38,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; +import com.android.systemui.doze.DozeMachine.State; import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.Assert; @@ -70,7 +71,6 @@ public class DozeTriggers implements DozeMachine.Part { * Assuming that the screen should start on. */ private static boolean sWakeDisplaySensorState = true; - private Runnable mQuickPickupDozeCancellable; private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500; @@ -96,6 +96,7 @@ public class DozeTriggers implements DozeMachine.Part { private long mNotificationPulseTime; private boolean mPulsePending; + private Runnable mAodInterruptRunnable; /** see {@link #onProximityFar} prox for callback */ private boolean mWantProxSensor; @@ -277,14 +278,14 @@ public class DozeTriggers implements DozeMachine.Part { boolean isWakeDisplayEvent = isQuickPickup || ((isWakeOnPresence || isWakeOnReach) && rawValues != null && rawValues.length > 0 && rawValues[0] != 0); - if (isWakeOnPresence || isQuickPickup) { - onWakeScreen(isQuickPickup || isWakeDisplayEvent, + if (isWakeOnPresence) { + onWakeScreen(isWakeDisplayEvent, mMachine.isExecutingTransition() ? null : mMachine.getState(), pulseReason); } else if (isLongPress) { requestPulse(pulseReason, true /* alreadyPerformedProxCheck */, null /* onPulseSuppressedListener */); - } else if (isWakeOnReach) { + } else if (isWakeOnReach || isQuickPickup) { if (isWakeDisplayEvent) { requestPulse(pulseReason, true /* alreadyPerformedProxCheck */, null /* onPulseSuppressedListener */); @@ -303,11 +304,16 @@ public class DozeTriggers implements DozeMachine.Part { } else if (isPickup) { gentleWakeUp(pulseReason); } else if (isUdfpsLongPress) { + final State state = mMachine.getState(); + if (state == State.DOZE_AOD || state == State.DOZE) { + // Since the gesture won't be received by the UDFPS view, we need to + // manually inject an event once the display is ON + mAodInterruptRunnable = () -> + mAuthController.onAodInterrupt((int) screenX, (int) screenY, + rawValues[3] /* major */, rawValues[4] /* minor */); + } + requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null); - // Since the gesture won't be received by the UDFPS view, manually inject an - // event. - mAuthController.onAodInterrupt((int) screenX, (int) screenY, - rawValues[3] /* major */, rawValues[4] /* minor */); } else { mDozeHost.extendPulse(pulseReason); } @@ -381,11 +387,7 @@ public class DozeTriggers implements DozeMachine.Part { */ private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state, int reason) { mDozeLog.traceWakeDisplay(wake, reason); - final boolean isWakeOnPresence = reason == DozeLog.REASON_SENSOR_WAKE_UP; - final boolean isQuickPickup = reason == DozeLog.REASON_SENSOR_QUICK_PICKUP; - if (isWakeOnPresence) { - sWakeDisplaySensorState = wake; - } + sWakeDisplaySensorState = wake; if (wake) { proximityCheckThenCall((result) -> { @@ -398,27 +400,13 @@ public class DozeTriggers implements DozeMachine.Part { // Log sensor triggered Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason)) .ifPresent(mUiEventLogger::log); - - if (isQuickPickup) { - // schedule runnable to go back to DOZE - onQuickPickup(); - } - } else if (state == DozeMachine.State.DOZE_AOD && isQuickPickup) { - // elongate time in DOZE_AOD, schedule new runnable to go back to DOZE - onQuickPickup(); } - }, isQuickPickup /* alreadyPerformedProxCheck */, reason); + }, false /* alreadyPerformedProxCheck */, reason); } else { boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED); boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING); - boolean pulse = (state == DozeMachine.State.DOZE_REQUEST_PULSE) - || (state == DozeMachine.State.DOZE_PULSING) - || (state == DozeMachine.State.DOZE_PULSING_BRIGHT); - boolean docked = (state == DozeMachine.State.DOZE_AOD_DOCKED); + if (!pausing && !paused) { - if (isQuickPickup && (pulse || docked)) { - return; - } mMachine.requestState(DozeMachine.State.DOZE); // log wake timeout mUiEventLogger.log(DozingUpdateUiEvent.DOZING_UPDATE_WAKE_TIMEOUT); @@ -426,19 +414,11 @@ public class DozeTriggers implements DozeMachine.Part { } } - private void onQuickPickup() { - cancelQuickPickupDelayableDoze(); - mQuickPickupDozeCancellable = mMainExecutor.executeDelayed(() -> { - onWakeScreen(false, - mMachine.isExecutingTransition() ? null : mMachine.getState(), - DozeLog.REASON_SENSOR_QUICK_PICKUP); - }, mDozeParameters.getQuickPickupAodDuration()); - } - @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case INITIALIZED: + mAodInterruptRunnable = null; sWakeDisplaySensorState = true; mBroadcastReceiver.register(mBroadcastDispatcher); mDozeHost.addCallback(mHostCallback); @@ -448,6 +428,7 @@ public class DozeTriggers implements DozeMachine.Part { break; case DOZE: case DOZE_AOD: + mAodInterruptRunnable = null; mWantProxSensor = newState != DozeMachine.State.DOZE; mWantSensors = true; mWantTouchScreenSensors = true; @@ -472,7 +453,6 @@ public class DozeTriggers implements DozeMachine.Part { mDozeSensors.requestTemporaryDisable(); break; case FINISH: - cancelQuickPickupDelayableDoze(); mBroadcastReceiver.unregister(mBroadcastDispatcher); mDozeHost.removeCallback(mHostCallback); mDockManager.removeListener(mDockEventListener); @@ -494,19 +474,14 @@ public class DozeTriggers implements DozeMachine.Part { || state == Display.STATE_DOZE_SUSPEND || state == Display.STATE_OFF; mDozeSensors.setProxListening(mWantProxSensor && lowPowerStateOrOff); mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors, lowPowerStateOrOff); - } - /** - * Cancels last scheduled Runnable that transitions to STATE_DOZE (blank screen) after - * going into STATE_AOD (AOD screen) from the quick pickup gesture. - */ - private void cancelQuickPickupDelayableDoze() { - if (mQuickPickupDozeCancellable != null) { - mQuickPickupDozeCancellable.run(); - mQuickPickupDozeCancellable = null; + if (mAodInterruptRunnable != null && state == Display.STATE_ON) { + mAodInterruptRunnable.run(); + mAodInterruptRunnable = null; } } + private void checkTriggersAtInit() { if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR || mDozeHost.isBlockingDoze() @@ -576,6 +551,8 @@ public class DozeTriggers implements DozeMachine.Part { @Override public void dump(PrintWriter pw) { + pw.println(" mAodInterruptRunnable=" + mAodInterruptRunnable); + pw.print(" notificationPulseTime="); pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime)); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index ff3cb2102d60..fbe06b02d955 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -21,6 +21,7 @@ import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; import android.app.AlarmManager; import android.content.Context; +import android.content.res.Configuration; import android.os.Handler; import android.os.SystemClock; import android.provider.Settings; @@ -34,6 +35,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.WakeLock; @@ -48,7 +50,8 @@ import dagger.Lazy; * The policy controlling doze. */ @DozeScope -public class DozeUi implements DozeMachine.Part, TunerService.Tunable { +public class DozeUi implements DozeMachine.Part, TunerService.Tunable, + ConfigurationController.ConfigurationListener { // if enabled, calls dozeTimeTick() whenever the time changes: private static final boolean BURN_IN_TESTING_ENABLED = false; private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min @@ -63,6 +66,7 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable { private final DozeLog mDozeLog; private final Lazy<StatusBarStateController> mStatusBarStateController; private final TunerService mTunerService; + private final ConfigurationController mConfigurationController; private boolean mKeyguardShowing; private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback = @@ -84,6 +88,11 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable { mHandler.post(mWakeLock.wrap(() -> {})); } } + + @Override + public void onShadeExpandedChanged(boolean expanded) { + updateAnimateScreenOff(); + } }; private long mLastTimeTickElapsed = 0; @@ -93,7 +102,8 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable { WakeLock wakeLock, DozeHost host, @Main Handler handler, DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor, DozeLog dozeLog, TunerService tunerService, - Lazy<StatusBarStateController> statusBarStateController) { + Lazy<StatusBarStateController> statusBarStateController, + ConfigurationController configurationController) { mContext = context; mWakeLock = wakeLock; mHost = host; @@ -107,11 +117,15 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable { mStatusBarStateController = statusBarStateController; mTunerService.addTunable(this, Settings.Secure.DOZE_ALWAYS_ON); + + mConfigurationController = configurationController; + mConfigurationController.addCallback(this); } @Override public void destroy() { mTunerService.removeTunable(this); + mConfigurationController.removeCallback(this); } @Override @@ -274,4 +288,9 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable { updateAnimateScreenOff(); } } + + @Override + public void onConfigChanged(Configuration newConfig) { + updateAnimateScreenOff(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index dfd85fe4dc90..bb44b09f1bce 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -30,6 +30,7 @@ import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.Context; import android.content.DialogInterface; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.drawable.Drawable; @@ -72,6 +73,7 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.telephony.TelephonyListenerManager; @@ -112,47 +114,101 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite @VisibleForTesting boolean mShowLockScreenCards = false; + private final KeyguardStateController.Callback mKeyguardStateControllerListener = + new KeyguardStateController.Callback() { + @Override + public void onUnlockedChanged() { + if (mDialog != null) { + ActionsDialog dialog = (ActionsDialog) mDialog; + boolean unlocked = mKeyguardStateController.isUnlocked(); + if (dialog.mWalletViewController != null) { + dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked); + } + + if (unlocked) { + dialog.hideLockMessage(); + } + } + } + }; + + private final ContentObserver mSettingsObserver = new ContentObserver(mMainHandler) { + @Override + public void onChange(boolean selfChange) { + onPowerMenuLockScreenSettingsChanged(); + } + }; + /** * @param context everything needs a context :( */ @Inject - public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs, - AudioManager audioManager, IDreamManager iDreamManager, - DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils, + public GlobalActionsDialog( + Context context, + GlobalActionsManager windowManagerFuncs, + AudioManager audioManager, + IDreamManager iDreamManager, + DevicePolicyManager devicePolicyManager, + LockPatternUtils lockPatternUtils, BroadcastDispatcher broadcastDispatcher, TelephonyListenerManager telephonyListenerManager, - GlobalSettings globalSettings, SecureSettings secureSettings, - @Nullable Vibrator vibrator, @Main Resources resources, - ConfigurationController configurationController, ActivityStarter activityStarter, - KeyguardStateController keyguardStateController, UserManager userManager, - TrustManager trustManager, IActivityManager iActivityManager, - @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, - NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor, + GlobalSettings globalSettings, + SecureSettings secureSettings, + @Nullable Vibrator vibrator, + @Main Resources resources, + ConfigurationController configurationController, + ActivityStarter activityStarter, + KeyguardStateController keyguardStateController, + UserManager userManager, + TrustManager trustManager, + IActivityManager iActivityManager, + @Nullable TelecomManager telecomManager, + MetricsLogger metricsLogger, + NotificationShadeDepthController depthController, + SysuiColorExtractor colorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, IWindowManager iWindowManager, @Background Executor backgroundExecutor, UiEventLogger uiEventLogger, - RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) { - - super(context, windowManagerFuncs, - audioManager, iDreamManager, - devicePolicyManager, lockPatternUtils, - broadcastDispatcher, telephonyListenerManager, - globalSettings, secureSettings, - vibrator, resources, + RingerModeTracker ringerModeTracker, + SysUiState sysUiState, + @Main Handler handler, + PackageManager packageManager, + StatusBar statusBar) { + + super(context, + windowManagerFuncs, + audioManager, + iDreamManager, + devicePolicyManager, + lockPatternUtils, + broadcastDispatcher, + telephonyListenerManager, + globalSettings, + secureSettings, + vibrator, + resources, configurationController, - keyguardStateController, userManager, - trustManager, iActivityManager, - telecomManager, metricsLogger, - depthController, colorExtractor, + keyguardStateController, + userManager, + trustManager, + iActivityManager, + telecomManager, + metricsLogger, + depthController, + colorExtractor, statusBarService, notificationShadeWindowController, iWindowManager, backgroundExecutor, uiEventLogger, null, - ringerModeTracker, sysUiState, handler); + ringerModeTracker, + sysUiState, + handler, + packageManager, + statusBar); mLockPatternUtils = lockPatternUtils; mKeyguardStateController = keyguardStateController; @@ -162,34 +218,22 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite mNotificationShadeWindowController = notificationShadeWindowController; mSysUiState = sysUiState; mActivityStarter = activityStarter; - keyguardStateController.addCallback(new KeyguardStateController.Callback() { - @Override - public void onUnlockedChanged() { - if (mDialog != null) { - ActionsDialog dialog = (ActionsDialog) mDialog; - boolean unlocked = mKeyguardStateController.isUnlocked(); - if (dialog.mWalletViewController != null) { - dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked); - } - if (unlocked) { - dialog.hideLockMessage(); - } - } - } - }); + mKeyguardStateController.addCallback(mKeyguardStateControllerListener); // Listen for changes to show pay on the power menu while locked onPowerMenuLockScreenSettingsChanged(); mGlobalSettings.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), false /* notifyForDescendants */, - new ContentObserver(handler) { - @Override - public void onChange(boolean selfChange) { - onPowerMenuLockScreenSettingsChanged(); - } - }); + mSettingsObserver); + } + + @Override + public void destroy() { + super.destroy(); + mKeyguardStateController.removeCallback(mKeyguardStateControllerListener); + mGlobalSettings.unregisterContentObserver(mSettingsObserver); } /** @@ -227,7 +271,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter, this::getWalletViewController, mDepthController, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, - mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger()); + mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger(), + getStatusBar()); if (shouldShowLockMessage(dialog)) { dialog.showLockMessage(); @@ -295,11 +340,13 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing, - MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) { + MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, + StatusBar statusBar) { super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions, adapter, overflowAdapter, depthController, sysuiColorExtractor, statusBarService, notificationShadeWindowController, sysuiState, - onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger, null); + onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger, null, + statusBar); mWalletFactory = walletFactory; // Update window attributes diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index 8e152830e208..f30d6b13ad02 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -74,8 +74,10 @@ import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.Log; import android.view.ContextThemeWrapper; +import android.view.GestureDetector; import android.view.IWindowManager; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.Window; @@ -119,6 +121,7 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.scrim.ScrimDrawable; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.telephony.TelephonyListenerManager; @@ -174,6 +177,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private final IDreamManager mDreamManager; private final DevicePolicyManager mDevicePolicyManager; private final LockPatternUtils mLockPatternUtils; + private final TelephonyListenerManager mTelephonyListenerManager; private final KeyguardStateController mKeyguardStateController; private final BroadcastDispatcher mBroadcastDispatcher; protected final GlobalSettings mGlobalSettings; @@ -228,6 +232,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms protected Handler mMainHandler; private int mSmallestScreenWidthDp; + private final StatusBar mStatusBar; @VisibleForTesting public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum { @@ -304,31 +309,46 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene * @param context everything needs a context :( */ @Inject - public GlobalActionsDialogLite(Context context, GlobalActionsManager windowManagerFuncs, - AudioManager audioManager, IDreamManager iDreamManager, - DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils, + public GlobalActionsDialogLite( + Context context, + GlobalActionsManager windowManagerFuncs, + AudioManager audioManager, + IDreamManager iDreamManager, + DevicePolicyManager devicePolicyManager, + LockPatternUtils lockPatternUtils, BroadcastDispatcher broadcastDispatcher, TelephonyListenerManager telephonyListenerManager, - GlobalSettings globalSettings, SecureSettings secureSettings, - @Nullable Vibrator vibrator, @Main Resources resources, + GlobalSettings globalSettings, + SecureSettings secureSettings, + @Nullable Vibrator vibrator, + @Main Resources resources, ConfigurationController configurationController, - KeyguardStateController keyguardStateController, UserManager userManager, - TrustManager trustManager, IActivityManager iActivityManager, - @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, - NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor, + KeyguardStateController keyguardStateController, + UserManager userManager, + TrustManager trustManager, + IActivityManager iActivityManager, + @Nullable TelecomManager telecomManager, + MetricsLogger metricsLogger, + NotificationShadeDepthController depthController, + SysuiColorExtractor colorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, IWindowManager iWindowManager, @Background Executor backgroundExecutor, UiEventLogger uiEventLogger, GlobalActionsInfoProvider infoProvider, - RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) { + RingerModeTracker ringerModeTracker, + SysUiState sysUiState, + @Main Handler handler, + PackageManager packageManager, + StatusBar statusBar) { mContext = context; mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; mDreamManager = iDreamManager; mDevicePolicyManager = devicePolicyManager; mLockPatternUtils = lockPatternUtils; + mTelephonyListenerManager = telephonyListenerManager; mKeyguardStateController = keyguardStateController; mBroadcastDispatcher = broadcastDispatcher; mGlobalSettings = globalSettings; @@ -351,7 +371,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mRingerModeTracker = ringerModeTracker; mSysUiState = sysUiState; mMainHandler = handler; - mSmallestScreenWidthDp = mContext.getResources().getConfiguration().smallestScreenWidthDp; + mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp; + mStatusBar = statusBar; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -360,11 +381,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter); - mHasTelephony = - context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); + mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); // get notified of phone state changes - telephonyListenerManager.addServiceStateListener(mPhoneStateListener); + mTelephonyListenerManager.addServiceStateListener(mPhoneStateListener); mGlobalSettings.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, mAirplaneModeObserver); @@ -384,6 +404,16 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mConfigurationController.addCallback(this); } + /** + * Clean up callbacks + */ + public void destroy() { + mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); + mTelephonyListenerManager.removeServiceStateListener(mPhoneStateListener); + mGlobalSettings.unregisterContentObserver(mAirplaneModeObserver); + mConfigurationController.removeCallback(this); + } + protected Context getContext() { return mContext; } @@ -392,6 +422,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene return mUiEventLogger; } + protected StatusBar getStatusBar() { + return mStatusBar; + } + /** * Show the global actions dialog (creating if necessary) * @@ -625,7 +659,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mDepthController, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger, - mInfoProvider); + mInfoProvider, mStatusBar); dialog.setOnDismissListener(this); dialog.setOnShowListener(this); @@ -679,14 +713,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mDialog.refreshDialog(); } } - - /** - * Clean up callbacks - */ - public void destroy() { - mConfigurationController.removeCallback(this); - } - /** * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is * called when the quick access wallet requests dismissal. @@ -834,6 +860,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU); mUiEventLogger.log(GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS); if (mTelecomManager != null) { + // Close shade so user sees the activity + mStatusBar.collapseShade(); Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent( null /* number */); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK @@ -964,6 +992,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene Log.w(TAG, "Bugreport handler could not be launched"); mIActivityManager.requestInteractiveBugReport(); } + // Close shade so user sees the activity + mStatusBar.collapseShade(); } catch (RemoteException e) { } } @@ -982,6 +1012,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mMetricsLogger.action(MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL); mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS); mIActivityManager.requestFullBugReport(); + // Close shade so user sees the activity + mStatusBar.collapseShade(); } catch (RemoteException e) { } return false; @@ -2008,7 +2040,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene } }; - private ContentObserver mAirplaneModeObserver = new ContentObserver(mMainHandler) { + private final ContentObserver mAirplaneModeObserver = new ContentObserver(mMainHandler) { @Override public void onChange(boolean selfChange) { onAirplaneModeChanged(); @@ -2100,9 +2132,53 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene protected final Runnable mOnRotateCallback; private UiEventLogger mUiEventLogger; private GlobalActionsInfoProvider mInfoProvider; + private GestureDetector mGestureDetector; + private StatusBar mStatusBar; protected ViewGroup mContainer; + @VisibleForTesting + protected GestureDetector.SimpleOnGestureListener mGestureListener = + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDown(MotionEvent e) { + // All gestures begin with this message, so continue listening + return true; + } + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + // Close without opening shade + mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); + cancel(); + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + if (distanceY < 0 && distanceY > distanceX + && e1.getY() <= mStatusBar.getStatusBarHeight()) { + // Downwards scroll from top + openShadeAndDismiss(); + return true; + } + return false; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX) + && e1.getY() <= mStatusBar.getStatusBarHeight()) { + // Downwards fling from top + openShadeAndDismiss(); + return true; + } + return false; + } + }; + ActionsDialogLite(Context context, int themeRes, MyAdapter adapter, MyOverflowAdapter overflowAdapter, NotificationShadeDepthController depthController, @@ -2110,7 +2186,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene NotificationShadeWindowController notificationShadeWindowController, SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing, MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, - @Nullable GlobalActionsInfoProvider infoProvider) { + @Nullable GlobalActionsInfoProvider infoProvider, StatusBar statusBar) { super(context, themeRes); mContext = context; mAdapter = adapter; @@ -2125,6 +2201,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mKeyguardShowing = keyguardShowing; mUiEventLogger = uiEventLogger; mInfoProvider = infoProvider; + mStatusBar = statusBar; + + mGestureDetector = new GestureDetector(mContext, mGestureListener); // Window initialization Window window = getWindow(); @@ -2146,6 +2225,23 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene initializeLayout(); } + @Override + public boolean onTouchEvent(MotionEvent event) { + return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event); + } + + private void openShadeAndDismiss() { + mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); + if (mStatusBar.isKeyguardShowing()) { + // match existing lockscreen behavior to open QS when swiping from status bar + mStatusBar.animateExpandSettingsPanel(null); + } else { + // otherwise, swiping down should expand notification shade + mStatusBar.animateExpandNotificationsPanel(); + } + dismiss(); + } + private ListPopupWindow createPowerOverflowPopup() { GlobalActionsPopupMenu popup = new GlobalActionsPopupMenu( new ContextThemeWrapper( @@ -2194,9 +2290,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mGlobalActionsLayout.setRotationListener(this::onRotate); mGlobalActionsLayout.setAdapter(mAdapter); mContainer = findViewById(com.android.systemui.R.id.global_actions_container); - mContainer.setOnClickListener(v -> { - mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); - cancel(); + mContainer.setOnTouchListener((v, event) -> { + mGestureDetector.onTouchEvent(event); + return v.onTouchEvent(event); }); View overflowButton = findViewById( diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 178a74cecc2e..e37d3d586ccc 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -32,7 +32,6 @@ import android.widget.TextView; import com.android.internal.R; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; -import com.android.systemui.Dependency; import com.android.systemui.plugins.GlobalActions; import com.android.systemui.scrim.ScrimDrawable; import com.android.systemui.statusbar.BlurUtils; @@ -52,19 +51,24 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks private final KeyguardStateController mKeyguardStateController; private final DeviceProvisionedController mDeviceProvisionedController; private final BlurUtils mBlurUtils; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final CommandQueue mCommandQueue; private GlobalActionsDialogLite mGlobalActionsDialog; private boolean mDisabled; @Inject public GlobalActionsImpl(Context context, CommandQueue commandQueue, - Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils) { + Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils, + KeyguardStateController keyguardStateController, + DeviceProvisionedController deviceProvisionedController, + KeyguardUpdateMonitor keyguardUpdateMonitor) { mContext = context; mGlobalActionsDialogLazy = globalActionsDialogLazy; - mKeyguardStateController = Dependency.get(KeyguardStateController.class); - mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); + mKeyguardStateController = keyguardStateController; + mDeviceProvisionedController = deviceProvisionedController; mCommandQueue = commandQueue; mBlurUtils = blurUtils; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; mCommandQueue.addCallback(this); } @@ -83,7 +87,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks mGlobalActionsDialog = mGlobalActionsDialogLazy.get(); mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(), mDeviceProvisionedController.isDeviceProvisioned()); - Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth(); + mKeyguardUpdateMonitor.requestFaceAuth(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt index 39008eecd6a2..17b532a643cd 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt @@ -25,6 +25,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import android.widget.ImageView +import android.widget.TextView import com.android.systemui.R import com.android.systemui.controls.controller.ControlsController import com.android.systemui.plugins.ActivityStarter @@ -70,6 +71,11 @@ class GlobalActionsInfoProvider @Inject constructor( val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel, parent, false) + + val walletTitle = walletClient.serviceLabel ?: context.getString(R.string.wallet_title) + val message = view.findViewById<TextView>(R.id.global_actions_change_message) + message?.setText(context.getString(R.string.global_actions_change_description, walletTitle)) + val button = view.findViewById<ImageView>(R.id.global_actions_change_button) button.setOnClickListener { _ -> dismissParent.run() diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 01a353ce8f1f..d30783c29f92 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -46,6 +46,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer { private final ImageGLWallpaper mWallpaper; private final Rect mSurfaceSize = new Rect(); private final WallpaperTexture mTexture; + private Consumer<Bitmap> mOnBitmapUpdated; public ImageWallpaperRenderer(Context context) { final WallpaperManager wpm = context.getSystemService(WallpaperManager.class); @@ -60,10 +61,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer { /** * @hide - * @return */ - public void useBitmap(Consumer<Bitmap> c) { - mTexture.use(c); + public void setOnBitmapChanged(Consumer<Bitmap> c) { + mOnBitmapUpdated = c; } @Override @@ -80,6 +80,8 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer { mTexture.use(bitmap -> { if (bitmap == null) { Log.w(TAG, "reload texture failed!"); + } else if (mOnBitmapUpdated != null) { + mOnBitmapUpdated.accept(bitmap); } mWallpaper.setup(bitmap); }); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java index bec4ce6ba658..fc5f3b8ae994 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java @@ -20,7 +20,6 @@ import android.annotation.Nullable; import android.content.res.ColorStateList; import android.graphics.Color; import android.text.TextUtils; -import android.view.View; import androidx.annotation.IntDef; @@ -202,10 +201,7 @@ public class KeyguardIndicationRotateTextViewController extends mCurrIndicationType = type; mIndicationQueue.removeIf(x -> x == type); - if (mCurrIndicationType == INDICATION_TYPE_NONE) { - mView.setVisibility(View.GONE); - } else { - mView.setVisibility(View.VISIBLE); + if (mCurrIndicationType != INDICATION_TYPE_NONE) { mIndicationQueue.add(type); // re-add to show later } @@ -299,7 +295,7 @@ public class KeyguardIndicationRotateTextViewController extends } } - private static final int INDICATION_TYPE_NONE = -1; + static final int INDICATION_TYPE_NONE = -1; public static final int INDICATION_TYPE_OWNER_INFO = 0; public static final int INDICATION_TYPE_DISCLOSURE = 1; public static final int INDICATION_TYPE_LOGOUT = 2; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 327698392ac7..88e9f69620ed 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -119,6 +119,7 @@ import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; import java.io.FileDescriptor; @@ -257,6 +258,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, /** TrustManager for letting it know when we change visibility */ private final TrustManager mTrustManager; + /** UserSwitcherController for creating guest user on boot complete */ + private final UserSwitcherController mUserSwitcherController; + /** * Used to keep the device awake while to ensure the keyguard finishes opening before * we sleep. @@ -805,6 +809,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager, @UiBackground Executor uiBgExecutor, PowerManager powerManager, TrustManager trustManager, + UserSwitcherController userSwitcherController, DeviceConfigProxy deviceConfig, NavigationModeController navigationModeController, KeyguardDisplayManager keyguardDisplayManager, @@ -825,6 +830,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, mUpdateMonitor = keyguardUpdateMonitor; mPM = powerManager; mTrustManager = trustManager; + mUserSwitcherController = userSwitcherController; mKeyguardDisplayManager = keyguardDisplayManager; dumpManager.registerDumpable(getClass().getName(), this); mDeviceConfig = deviceConfig; @@ -2558,6 +2564,11 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, @Override public void onBootCompleted() { synchronized (this) { + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_guestUserAutoCreated)) { + // TODO(b/191067027): Move post-boot guest creation to system_server + mUserSwitcherController.guaranteeGuestPresent(); + } mBootCompleted = true; adjustStatusBarLocked(false, true); if (mBootSendUserPresent) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java index 2e03d9a90f49..6f878d19aa4b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java @@ -67,7 +67,7 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe @Nullable private final IWallpaperManager mWallpaperManagerService; - private int mWakefulness = WAKEFULNESS_ASLEEP; + private int mWakefulness = WAKEFULNESS_AWAKE; private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 05d1361c3ab8..8a383b974d77 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -54,6 +54,7 @@ import com.android.systemui.statusbar.phone.KeyguardLiftController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.settings.GlobalSettings; @@ -92,6 +93,7 @@ public class KeyguardModule { DumpManager dumpManager, PowerManager powerManager, TrustManager trustManager, + UserSwitcherController userSwitcherController, @UiBackground Executor uiBgExecutor, DeviceConfigProxy deviceConfig, NavigationModeController navigationModeController, @@ -114,6 +116,7 @@ public class KeyguardModule { uiBgExecutor, powerManager, trustManager, + userSwitcherController, deviceConfig, navigationModeController, keyguardDisplayManager, diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt index 77d789292e5e..2bf102f724f4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt @@ -186,11 +186,9 @@ class KeyguardMediaController @Inject constructor( } private fun hideMediaPlayer() { - if (useSplitShade) { - setVisibility(splitShadeContainer, View.GONE) - } else { - setVisibility(singlePaneContainer, View.GONE) - } + // always hide splitShadeContainer as it's initially visible and may influence layout + setVisibility(splitShadeContainer, View.GONE) + setVisibility(singlePaneContainer, View.GONE) } private fun setVisibility(view: ViewGroup?, newVisibility: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 1004e257c750..3251ab2e4d50 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -156,6 +156,12 @@ class MediaCarouselController @Inject constructor( } } + /** + * Update MediaCarouselScrollHandler.visibleToUser to reflect media card container visibility. + * It will be called when the container is out of view. + */ + lateinit var updateUserVisibility: () -> Unit + init { mediaFrame = inflateMediaCarousel() mediaCarousel = mediaFrame.requireViewById(R.id.media_carousel_scroller) @@ -177,6 +183,12 @@ class MediaCarouselController @Inject constructor( keysNeedRemoval.forEach { removePlayer(it) } keysNeedRemoval.clear() + // Update user visibility so that no extra impression will be logged when + // activeMediaIndex resets to 0 + if (this::updateUserVisibility.isInitialized) { + updateUserVisibility() + } + // Let's reset our scroll position mediaCarouselScrollHandler.scrollToStart() } @@ -187,16 +199,24 @@ class MediaCarouselController @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (addOrUpdatePlayer(key, oldKey, data)) { MediaPlayerData.getMediaPlayer(key, null)?.let { logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED it.mInstanceId, /* isRecommendationCard */ false, - it.surfaceForSmartspaceLogging) + it.surfaceForSmartspaceLogging, + rank = MediaPlayerData.getMediaPlayerIndex(key)) } } + if (mediaCarouselScrollHandler.visibleToUser && + isSsReactivated && !mediaCarouselScrollHandler.qsExpanded) { + // It could happen that reactived media player isn't visible to user because + // of it is a resumption card. + logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded) + } val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active if (canRemove && !Utils.useMediaResumption(context)) { // This view isn't playing, let's remove this! This happens e.g when @@ -224,10 +244,17 @@ class MediaCarouselController @Inject constructor( logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED it.mInstanceId, /* isRecommendationCard */ true, - it.surfaceForSmartspaceLogging) - } - if (mediaCarouselScrollHandler.visibleToUser) { - logSmartspaceImpression() + it.surfaceForSmartspaceLogging, + rank = MediaPlayerData.getMediaPlayerIndex(key)) + + if (mediaCarouselScrollHandler.visibleToUser && + mediaCarouselScrollHandler.visibleMediaIndex == + MediaPlayerData.getMediaPlayerIndex(key)) { + logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN + it.mInstanceId, + /* isRecommendationCard */ true, + it.surfaceForSmartspaceLogging) + } } } else { onSmartspaceMediaDataRemoved(data.targetId, immediately = true) @@ -644,17 +671,17 @@ class MediaCarouselController @Inject constructor( } /** - * Log the user impression for media card. + * Log the user impression for media card at visibleMediaIndex. */ - fun logSmartspaceImpression() { + fun logSmartspaceImpression(qsExpanded: Boolean) { val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex if (MediaPlayerData.players().size > visibleMediaIndex) { val mediaControlPanel = MediaPlayerData.players().elementAt(visibleMediaIndex) - val isMediaActive = - MediaPlayerData.playerKeys().elementAt(visibleMediaIndex).data?.active + val hasActiveMediaOrRecommendationCard = + MediaPlayerData.hasActiveMediaOrRecommendationCard() val isRecommendationCard = mediaControlPanel.recommendationViewHolder != null - if (!isRecommendationCard && !isMediaActive) { - // Media control card time out or swiped away + if (!hasActiveMediaOrRecommendationCard && !qsExpanded) { + // Skip logging if on LS or QQS, and there is no active media card return } logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN @@ -672,6 +699,13 @@ class MediaCarouselController @Inject constructor( surface: Int, rank: Int = mediaCarouselScrollHandler.visibleMediaIndex ) { + // Only log media resume card when Smartspace data is available + if (!isRecommendationCard && + !mediaManager.smartspaceMediaData.isActive && + MediaPlayerData.smartspaceMediaData == null) { + return + } + /* ktlint-disable max-line-length */ SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED, eventId, @@ -770,6 +804,16 @@ internal object MediaPlayerData { return mediaData.get(key)?.let { mediaPlayers.get(it) } } + fun getMediaPlayerIndex(key: String): Int { + val sortKey = mediaData.get(key) + mediaPlayers.entries.forEachIndexed { index, e -> + if (e.key == sortKey) { + return index + } + } + return -1 + } + fun removeMediaPlayer(key: String) = mediaData.remove(key)?.let { if (it.isSsMediaRec) { smartspaceMediaData = null @@ -808,4 +852,15 @@ internal object MediaPlayerData { mediaData.clear() mediaPlayers.clear() } + + /* Returns true if there is active media player card or recommendation card */ + fun hasActiveMediaOrRecommendationCard(): Boolean { + if (smartspaceMediaData != null && smartspaceMediaData?.isActive!!) { + return true + } + if (firstActiveMediaIndex() != -1) { + return true + } + return false + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index eb3549789c19..b0d4cb1c9818 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -62,7 +62,7 @@ class MediaCarouselScrollHandler( private val closeGuts: (immediate: Boolean) -> Unit, private val falsingCollector: FalsingCollector, private val falsingManager: FalsingManager, - private val logSmartspaceImpression: () -> Unit + private val logSmartspaceImpression: (Boolean) -> Unit ) { /** * Is the view in RTL @@ -195,18 +195,22 @@ class MediaCarouselScrollHandler( if (playerWidthPlusPadding == 0) { return } + val relativeScrollX = scrollView.relativeScrollX onMediaScrollingChanged(relativeScrollX / playerWidthPlusPadding, relativeScrollX % playerWidthPlusPadding) } } + /** + * Whether the media card is visible to user if any + */ var visibleToUser: Boolean = false - set(value) { - if (field != value) { - field = value - } - } + + /** + * Whether the quick setting is expanded or not + */ + var qsExpanded: Boolean = false init { gestureDetector = GestureDetectorCompat(scrollView.context, gestureListener) @@ -471,7 +475,7 @@ class MediaCarouselScrollHandler( val oldIndex = visibleMediaIndex visibleMediaIndex = newIndex if (oldIndex != visibleMediaIndex && visibleToUser) { - logSmartspaceImpression() + logSmartspaceImpression(qsExpanded) } closeGuts(false) updatePlayerVisibilities() diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index c2b580773424..19a67e95a496 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -46,6 +46,7 @@ import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.constraintlayout.widget.ConstraintSet; +import com.android.internal.jank.InteractionJankMonitor; import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; @@ -265,7 +266,7 @@ public class MediaControlPanel { } mKey = key; MediaSession.Token token = data.getToken(); - mInstanceId = data.getPackageName().hashCode(); + mInstanceId = SmallHash.hash(data.getPackageName()); mBackgroundColor = data.getBackgroundColor(); if (mToken == null || !mToken.equals(token)) { @@ -468,7 +469,8 @@ public class MediaControlPanel { TransitionLayout player) { // TODO(b/174236650): Make sure that the carousel indicator also fades out. // TODO(b/174236650): Instrument the animation to measure jank. - return new GhostedViewLaunchAnimatorController(player) { + return new GhostedViewLaunchAnimatorController(player, + InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER) { @Override protected float getCurrentTopCornerRadius() { return ((IlluminationDrawable) player.getBackground()).getCornerRadius(); @@ -502,7 +504,7 @@ public class MediaControlPanel { return; } - mInstanceId = data.getTargetId().hashCode(); + mInstanceId = SmallHash.hash(data.getTargetId()); mBackgroundColor = data.getBackgroundColor(); TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations(); recommendationCard.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor)); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt index ee1d3ea87da8..296bfda89432 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -31,7 +31,8 @@ class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener, key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (oldKey != null && oldKey != key && entries.contains(oldKey)) { entries[key] = data to entries.remove(oldKey)?.second diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt index a611b600f47f..c8deb014f781 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt @@ -83,7 +83,8 @@ class MediaDataFilter @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (oldKey != null && oldKey != key) { allEntries.remove(oldKey) @@ -101,7 +102,7 @@ class MediaDataFilter @Inject constructor( // Notify listeners listeners.forEach { - it.onMediaDataLoaded(key, oldKey, data) + it.onMediaDataLoaded(key, oldKey, data, isSsReactivated = isSsReactivated) } } @@ -118,6 +119,8 @@ class MediaDataFilter @Inject constructor( // Override the pass-in value here, as the order of Smartspace card is only determined here. var shouldPrioritizeMutable = false smartspaceMediaData = data + // Override the pass-in value here, as the Smartspace reactivation could only happen here. + var isSsReactivated = false // Before forwarding the smartspace target, first check if we have recently inactive media val sorted = userEntries.toSortedMap(compareBy { @@ -137,9 +140,13 @@ class MediaDataFilter @Inject constructor( // Notify listeners to consider this media active Log.d(TAG, "reactivating $lastActiveKey instead of smartspace") reactivatedKey = lastActiveKey + if (MediaPlayerData.firstActiveMediaIndex() == -1) { + isSsReactivated = true + } val mediaData = sorted.get(lastActiveKey)!!.copy(active = true) listeners.forEach { - it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData) + it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData, + isSsReactivated = isSsReactivated) } } else { // Mark to prioritize Smartspace card if no recent media. diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 5b1e039ad0f8..ed6f5505cbd1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -39,6 +39,7 @@ import android.media.session.MediaSession import android.net.Uri import android.os.Parcelable import android.os.UserHandle +import android.provider.Settings import android.service.notification.StatusBarNotification import android.text.TextUtils import android.util.Log @@ -54,6 +55,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.BcSmartspaceDataPlugin import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState import com.android.systemui.statusbar.notification.row.HybridGroupManager +import com.android.systemui.tuner.TunerService import com.android.systemui.util.Assert import com.android.systemui.util.Utils import com.android.systemui.util.concurrency.DelayableExecutor @@ -114,7 +116,8 @@ class MediaDataManager( private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider, private var useMediaResumption: Boolean, private val useQsMediaPlayer: Boolean, - private val systemClock: SystemClock + private val systemClock: SystemClock, + private val tunerService: TunerService ) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener { companion object { @@ -145,8 +148,9 @@ class MediaDataManager( private val internalListeners: MutableSet<Listener> = mutableSetOf() private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() // There should ONLY be at most one Smartspace media recommendation. - private var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA + var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA private var smartspaceSession: SmartspaceSession? = null + private var allowMediaRecommendations = Utils.allowMediaRecommendations(context) @Inject constructor( @@ -164,12 +168,13 @@ class MediaDataManager( mediaDataFilter: MediaDataFilter, activityStarter: ActivityStarter, smartspaceMediaDataProvider: SmartspaceMediaDataProvider, - clock: SystemClock + clock: SystemClock, + tunerService: TunerService ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory, broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener, mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter, activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context), - Utils.useQsMediaPlayer(context), clock) + Utils.useQsMediaPlayer(context), clock, tunerService) private val appChangeReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { @@ -243,6 +248,14 @@ class MediaDataManager( }) } smartspaceSession?.let { it.requestSmartspaceUpdate() } + tunerService.addTunable(object : TunerService.Tunable { + override fun onTuningChanged(key: String?, newValue: String?) { + allowMediaRecommendations = Utils.allowMediaRecommendations(context) + if (!allowMediaRecommendations) { + dismissSmartspaceRecommendation(key = smartspaceMediaData.targetId, delay = 0L) + } + } + }, Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION) } fun destroy() { @@ -695,8 +708,7 @@ class MediaDataManager( } override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) { - if (!Utils.allowMediaRecommendations(context)) { - Log.d(TAG, "Smartspace recommendation is disabled in Settings.") + if (!allowMediaRecommendations) { return } @@ -812,12 +824,16 @@ class MediaDataManager( * @param immediately indicates should apply the UI changes immediately, otherwise wait * until the next refresh-round before UI becomes visible. True by default to take in place * immediately. + * + * @param isSsReactivated indicates transition from a state with no active media players to + * a state with active media players upon receiving Smartspace media data. */ fun onMediaDataLoaded( key: String, oldKey: String?, data: MediaData, - immediately: Boolean = true + immediately: Boolean = true, + isSsReactivated: Boolean = false ) {} /** diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index 52ecbea05924..292b0e291244 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -67,7 +67,8 @@ class MediaDeviceManager @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (oldKey != null && oldKey != key) { val oldEntry = entries.remove(oldKey) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 075bc700cfa0..3d1b4fbde56a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -187,6 +187,12 @@ class MediaHierarchyManager @Inject constructor( private var currentAttachmentLocation = -1 /** + * Is there any active media in the carousel? + */ + private var hasActiveMedia: Boolean = false + get() = mediaHosts.get(LOCATION_QQS)?.visible == true + + /** * Are we currently waiting on an animation to start? */ private var animationPending: Boolean = false @@ -214,14 +220,11 @@ class MediaHierarchyManager @Inject constructor( set(value) { if (field != value) { field = value + mediaCarouselController.mediaCarouselScrollHandler.qsExpanded = value } // qs is expanded on LS shade and HS shade if (value && (isLockScreenShadeVisibleToUser() || isHomeScreenShadeVisibleToUser())) { - mediaCarouselController.logSmartspaceImpression() - } - // Release shade and back to lock screen - if (isLockScreenVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() + mediaCarouselController.logSmartspaceImpression(value) } mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser() } @@ -403,7 +406,7 @@ class MediaHierarchyManager @Inject constructor( updateTargetState() // Enters shade from lock screen if (newState == StatusBarState.SHADE_LOCKED && isLockScreenShadeVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() + mediaCarouselController.logSmartspaceImpression(qsExpanded) } mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser() } @@ -417,7 +420,7 @@ class MediaHierarchyManager @Inject constructor( dozeAnimationRunning = false // Enters lock screen from screen off if (isLockScreenVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() + mediaCarouselController.logSmartspaceImpression(qsExpanded) } } else { updateDesiredLocation() @@ -430,11 +433,7 @@ class MediaHierarchyManager @Inject constructor( override fun onExpandedChanged(isExpanded: Boolean) { // Enters shade from home screen if (isHomeScreenShadeVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() - } - // Back to lock screen from bouncer - if (isLockScreenVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() + mediaCarouselController.logSmartspaceImpression(qsExpanded) } mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser() } @@ -459,6 +458,10 @@ class MediaHierarchyManager @Inject constructor( goingToSleep = false } }) + + mediaCarouselController.updateUserVisibility = { + mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser() + } } private fun updateConfiguration() { @@ -476,8 +479,12 @@ class MediaHierarchyManager @Inject constructor( val viewHost = createUniqueObjectHost() mediaObject.hostView = viewHost mediaObject.addVisibilityChangeListener { + // If QQS changes visibility, we need to force an update to ensure the transition + // goes into the correct state + val stateUpdate = mediaObject.location == LOCATION_QQS + // Never animate because of a visibility change, only state changes should do that - updateDesiredLocation(forceNoAnimation = true) + updateDesiredLocation(forceNoAnimation = true, forceStateUpdate = stateUpdate) } mediaHosts[mediaObject.location] = mediaObject if (mediaObject.location == desiredLocation) { @@ -521,10 +528,15 @@ class MediaHierarchyManager @Inject constructor( * going from the old desired location to the new one. * * @param forceNoAnimation optional parameter telling the system not to animate + * @param forceStateUpdate optional parameter telling the system to update transition state + * even if location did not change */ - private fun updateDesiredLocation(forceNoAnimation: Boolean = false) { + private fun updateDesiredLocation( + forceNoAnimation: Boolean = false, + forceStateUpdate: Boolean = false + ) { val desiredLocation = calculateLocation() - if (desiredLocation != this.desiredLocation) { + if (desiredLocation != this.desiredLocation || forceStateUpdate) { if (this.desiredLocation >= 0) { previousLocation = this.desiredLocation } @@ -784,7 +796,7 @@ class MediaHierarchyManager @Inject constructor( private fun getQSTransformationProgress(): Float { val currentHost = getHost(desiredLocation) val previousHost = getHost(previousLocation) - if (currentHost?.location == LOCATION_QS) { + if (hasActiveMedia && currentHost?.location == LOCATION_QS) { if (previousHost?.location == LOCATION_QQS) { if (previousHost.visible || statusbarState != StatusBarState.KEYGUARD) { return qsExpansion @@ -917,6 +929,7 @@ class MediaHierarchyManager @Inject constructor( val location = when { qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS qsExpansion > 0.4f && onLockscreen -> LOCATION_QS + !hasActiveMedia -> LOCATION_QS onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN else -> LOCATION_QQS diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index 43e21424c45e..ff085c36ef9c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -60,7 +60,8 @@ class MediaHost constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (immediately) { updateViewVisibility() diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index 0da84fbac600..ab568c8c5a85 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -159,7 +159,8 @@ class MediaResumeListener @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (useMediaResumption) { // If this had been started from a resume state, disconnect now that it's live diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt index a4f33e354b68..8bddde839817 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt @@ -95,7 +95,8 @@ class MediaSessionBasedFilter @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { backgroundExecutor.execute { data.token?.let { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt index bbea140ecfaf..9a3919326cbd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt @@ -54,7 +54,8 @@ class MediaTimeoutListener @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { var reusedListener: PlaybackStateListener? = null diff --git a/packages/SystemUI/src/com/android/systemui/media/SmallHash.java b/packages/SystemUI/src/com/android/systemui/media/SmallHash.java new file mode 100644 index 000000000000..de7aac609955 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/SmallHash.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media; + +import java.util.Objects; + +/** + * A simple hash function for use in privacy-sensitive logging. + */ +public final class SmallHash { + // Hashes will be in the range [0, MAX_HASH). + public static final int MAX_HASH = (1 << 13); + + /** Return Small hash of the string, if non-null, or 0 otherwise. */ + public static int hash(String in) { + return hash(Objects.hashCode(in)); + } + + /** + * Maps in to the range [0, MAX_HASH), keeping similar values distinct. + * + * @param in An arbitrary integer. + * @return in mod MAX_HASH, signs chosen to stay in the range [0, MAX_HASH). + */ + public static int hash(int in) { + return Math.abs(Math.floorMod(in, MAX_HASH)); + } + + private SmallHash() {} +} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 0d9749e05262..ff5d0b157c80 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -251,8 +251,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private float mMLResults; // For debugging - private ArrayDeque<String> mPredictionLog = new ArrayDeque<>(); - private ArrayDeque<String> mGestureLog = new ArrayDeque<>(); + private LogArray mPredictionLog = new LogArray(MAX_NUM_LOGGED_PREDICTIONS); + private LogArray mGestureLogInsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES); + private LogArray mGestureLogOutsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES); private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver; @@ -631,7 +632,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker return mMLResults >= mMLModelThreshold ? 1 : 0; } - private boolean isWithinTouchRegion(int x, int y) { + private boolean isWithinInsets(int x, int y) { // Disallow if we are in the bottom gesture area if (y >= (mDisplaySize.y - mBottomGestureHeight)) { return false; @@ -644,7 +645,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) { return false; } + return true; + } + private boolean isWithinTouchRegion(int x, int y) { // If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back // gesture final boolean isInsidePip = mIsInPipMode && mPipExcludedBounds.contains(x, y); @@ -675,14 +679,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker } // For debugging purposes - if (mPredictionLog.size() >= MAX_NUM_LOGGED_PREDICTIONS) { - mPredictionLog.removeFirst(); - } - mPredictionLog.addLast(String.format("Prediction [%d,%d,%d,%d,%f,%d]", + mPredictionLog.log(String.format("Prediction [%d,%d,%d,%d,%f,%d]", System.currentTimeMillis(), x, y, app, mMLResults, withinRange ? 1 : 0)); - if (DEBUG_MISSING_GESTURE) { - Log.d(DEBUG_MISSING_GESTURE_TAG, mPredictionLog.peekLast()); - } // Always allow if the user is in a transient sticky immersive state if (mIsNavBarShownTransiently) { @@ -755,7 +753,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mMLResults = 0; mLogGesture = false; mInRejectedExclusion = false; - mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed + boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY()); + mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets && !mGestureBlockingActivityRunning && !QuickStepContract.isBackGestureDisabled(mSysUiFlags) && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()); @@ -769,18 +768,13 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mThresholdCrossed = false; } - // For debugging purposes - if (mGestureLog.size() >= MAX_NUM_LOGGED_GESTURES) { - mGestureLog.removeFirst(); - } - mGestureLog.addLast(String.format( + // For debugging purposes, only log edge points + (isWithinInsets ? mGestureLogInsideInsets : mGestureLogOutsideInsets).log(String.format( "Gesture [%d,alw=%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]", - System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, mIsBackGestureAllowed, + System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, + mIsBackGestureAllowed, QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize, mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion)); - if (DEBUG_MISSING_GESTURE) { - Log.d(DEBUG_MISSING_GESTURE_TAG, mGestureLog.peekLast()); - } } else if (mAllowGesture || mLogGesture) { if (!mThresholdCrossed) { mEndPoint.x = (int) ev.getX(); @@ -907,7 +901,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker pw.println(" mUseMLModel=" + mUseMLModel); pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep); pw.println(" mStartingQuickstepRotation=" + mStartingQuickstepRotation); - pw.println(" mInRejectedExclusion" + mInRejectedExclusion); + pw.println(" mInRejectedExclusion=" + mInRejectedExclusion); pw.println(" mExcludeRegion=" + mExcludeRegion); pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion); pw.println(" mIsInPipMode=" + mIsInPipMode); @@ -922,7 +916,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker pw.println(" mTouchSlop=" + mTouchSlop); pw.println(" mBottomGestureHeight=" + mBottomGestureHeight); pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog)); - pw.println(" mGestureLog=" + String.join("\n", mGestureLog)); + pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets)); + pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets)); pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin); } @@ -945,4 +940,23 @@ public class EdgeBackGestureHandler extends CurrentUserTracker } proto.edgeBackGestureHandler.allowGesture = mAllowGesture; } + + + private static class LogArray extends ArrayDeque<String> { + private final int mLength; + + LogArray(int length) { + mLength = length; + } + + void log(String message) { + if (size() >= mLength) { + removeFirst(); + } + addLast(message); + if (DEBUG_MISSING_GESTURE) { + Log.d(DEBUG_MISSING_GESTURE_TAG, message); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java index b5ac90828fce..d863dcce4fc7 100644 --- a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java @@ -240,9 +240,16 @@ public class NotificationHelper { /** Returns whether {@code entry} is suppressed from shade, meaning we should not show it. */ public static boolean shouldFilterOut( Optional<Bubbles> bubblesOptional, NotificationEntry entry) { - return bubblesOptional.isPresent() - && bubblesOptional.get().isBubbleNotificationSuppressedFromShade( - entry.getKey(), entry.getSbn().getGroupKey()); + boolean isSuppressed = false; + //TODO(b/190822282): Investigate what is causing the NullPointerException + try { + isSuppressed = bubblesOptional.isPresent() + && bubblesOptional.get().isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getSbn().getGroupKey()); + } catch (Exception e) { + Log.e(TAG, "Exception checking if notification is suppressed: " + e); + } + return isSuppressed; } } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index e53cc0b72e3b..917a060f1f1d 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -494,7 +494,8 @@ public class PeopleSpaceUtils { * match the data by {@link ContactsContract.ContactsColumns#LOOKUP_KEY} key to ensure proper * matching across all the Contacts DB tables. */ - private static List<String> getContactLookupKeysWithBirthdaysToday(Context context) { + @VisibleForTesting + public static List<String> getContactLookupKeysWithBirthdaysToday(Context context) { List<String> lookupKeysWithBirthdaysToday = new ArrayList<>(1); String today = new SimpleDateFormat("MM-dd").format(new Date()); String[] projection = new String[]{ @@ -503,14 +504,20 @@ public class PeopleSpaceUtils { String where = ContactsContract.Data.MIMETYPE + "= ? AND " + ContactsContract.CommonDataKinds.Event.TYPE + "=" - + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + " AND substr(" - + ContactsContract.CommonDataKinds.Event.START_DATE + ",6) = ?"; + + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + " AND (substr(" + // Birthdays stored with years will match this format + + ContactsContract.CommonDataKinds.Event.START_DATE + ",6) = ? OR substr(" + // Birthdays stored without years will match this format + + ContactsContract.CommonDataKinds.Event.START_DATE + ",3) = ? )"; String[] selection = - new String[]{ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, today}; + new String[]{ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, today, + today}; Cursor cursor = null; try { - cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, where, selection, null); + cursor = context + .getContentResolver() + .query(ContactsContract.Data.CONTENT_URI, + projection, where, selection, null); while (cursor != null && cursor.moveToNext()) { String lookupKey = cursor.getString( cursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)); diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 32fdf0ea4de6..0e17c8bfcab5 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -265,7 +265,7 @@ public class PeopleTileViewHelper { private RemoteViews createSuppressedView() { RemoteViews views; - if (mTile.isUserQuieted()) { + if (mTile != null && mTile.isUserQuieted()) { views = new RemoteViews(mContext.getPackageName(), R.layout.people_tile_work_profile_quiet_layout); } else { diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 688884730373..4085df9a8093 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -22,6 +22,7 @@ import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE; import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.content.Intent.ACTION_BOOT_COMPLETED; +import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; import static com.android.systemui.people.NotificationHelper.getContactUri; @@ -62,6 +63,7 @@ import android.content.pm.ShortcutInfo; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; @@ -172,6 +174,7 @@ public class PeopleSpaceWidgetManager { public void init() { synchronized (mLock) { if (!mRegisteredReceivers) { + if (DEBUG) Log.d(TAG, "Register receivers"); IntentFilter filter = new IntentFilter(); filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); filter.addAction(ACTION_BOOT_COMPLETED); @@ -185,6 +188,14 @@ public class PeopleSpaceWidgetManager { mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter, null /* executor */, UserHandle.ALL); + IntentFilter perAppFilter = new IntentFilter(ACTION_PACKAGE_REMOVED); + perAppFilter.addDataScheme("package"); + // BroadcastDispatcher doesn't allow data schemes. + mContext.registerReceiver(mBaseBroadcastReceiver, perAppFilter); + IntentFilter bootComplete = new IntentFilter(ACTION_BOOT_COMPLETED); + bootComplete.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + // BroadcastDispatcher doesn't allow priority. + mContext.registerReceiver(mBaseBroadcastReceiver, bootComplete); mRegisteredReceivers = true; } } @@ -303,10 +314,6 @@ public class PeopleSpaceWidgetManager { /** Updates tile in app widget options and the current view. */ public void updateAppWidgetOptionsAndView(int appWidgetId, PeopleSpaceTile tile) { - if (tile == null) { - if (DEBUG) Log.w(TAG, "Requested to store null tile"); - return; - } synchronized (mTiles) { mTiles.put(appWidgetId, tile); } @@ -320,6 +327,17 @@ public class PeopleSpaceWidgetManager { */ @Nullable public PeopleSpaceTile getTileForExistingWidget(int appWidgetId) { + try { + return getTileForExistingWidgetThrowing(appWidgetId); + } catch (Exception e) { + Log.e(TAG, "Failed to retrieve conversation for tile: " + e); + return null; + } + } + + @Nullable + private PeopleSpaceTile getTileForExistingWidgetThrowing(int appWidgetId) throws + PackageManager.NameNotFoundException { // First, check if tile is cached in memory. PeopleSpaceTile tile; synchronized (mTiles) { @@ -348,7 +366,8 @@ public class PeopleSpaceWidgetManager { * If a {@link PeopleTileKey} is not provided, fetch one from {@link SharedPreferences}. */ @Nullable - public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) { + public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) throws + PackageManager.NameNotFoundException { if (!key.isValid()) { Log.e(TAG, "PeopleTileKey invalid: " + key.toString()); return null; @@ -358,7 +377,6 @@ public class PeopleSpaceWidgetManager { Log.d(TAG, "System services are null"); return null; } - try { if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + key.toString()); ConversationChannel channel = mIPeopleManager.getConversation( @@ -383,9 +401,9 @@ public class PeopleSpaceWidgetManager { } // Add current state. - return updateWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED); - } catch (Exception e) { - Log.e(TAG, "Failed to retrieve conversation for tile: " + e); + return getTileWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED); + } catch (RemoteException e) { + Log.e(TAG, "Could not retrieve data: " + e); return null; } } @@ -397,7 +415,6 @@ public class PeopleSpaceWidgetManager { public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { if (DEBUG) { - Log.d(TAG, "updateWidgetsWithNotificationChanged called"); if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { Log.d(TAG, "Notification posted, key: " + sbn.getKey()); } else { @@ -414,7 +431,6 @@ public class PeopleSpaceWidgetManager { PeopleTileKey key = new PeopleTileKey( sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName()); if (!key.isValid()) { - Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString()); return; } int[] widgetIds = mAppWidgetManager.getAppWidgetIds( @@ -775,7 +791,13 @@ public class PeopleSpaceWidgetManager { /** Adds a widget based on {@code key} mapped to {@code appWidgetId}. */ public void addNewWidget(int appWidgetId, PeopleTileKey key) { if (DEBUG) Log.d(TAG, "addNewWidget called with key for appWidgetId: " + appWidgetId); - PeopleSpaceTile tile = getTileFromPersistentStorage(key, appWidgetId); + PeopleSpaceTile tile = null; + try { + tile = getTileFromPersistentStorage(key, appWidgetId); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Cannot add widget since app was uninstalled"); + return; + } if (tile == null) { return; } @@ -1017,72 +1039,85 @@ public class PeopleSpaceWidgetManager { @Override public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (DEBUG) Log.d(TAG, "Update widgets from: " + action); - mBgExecutor.execute(() -> updateWidgetsOnStateChange(action)); + if (DEBUG) Log.d(TAG, "Update widgets from: " + intent.getAction()); + mBgExecutor.execute(() -> updateWidgetsFromBroadcastInBackground(intent.getAction())); } }; - /** Updates any app widget based on the current state. */ + /** Updates any app widget to the current state, triggered by a broadcast update. */ @VisibleForTesting - void updateWidgetsOnStateChange(String entryPoint) { + void updateWidgetsFromBroadcastInBackground(String entryPoint) { int[] appWidgetIds = mAppWidgetManager.getAppWidgetIds( new ComponentName(mContext, PeopleSpaceWidgetProvider.class)); if (appWidgetIds == null) { return; } - synchronized (mLock) { - for (int appWidgetId : appWidgetIds) { - PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId); - if (tile == null) { - Log.e(TAG, "Matching conversation not found for shortcut ID"); - } else { - tile = updateWithCurrentState(tile, entryPoint); + for (int appWidgetId : appWidgetIds) { + PeopleSpaceTile existingTile = null; + PeopleSpaceTile updatedTile = null; + try { + synchronized (mLock) { + existingTile = getTileForExistingWidgetThrowing(appWidgetId); + if (existingTile == null) { + Log.e(TAG, "Matching conversation not found for shortcut ID"); + return; + } + updatedTile = getTileWithCurrentState(existingTile, entryPoint); + updateAppWidgetOptionsAndView(appWidgetId, updatedTile); } - updateAppWidgetOptionsAndView(appWidgetId, tile); + } catch (PackageManager.NameNotFoundException e) { + // Delete data for uninstalled widgets. + Log.e(TAG, "Package no longer found for tile: " + e); + synchronized (mLock) { + updateAppWidgetOptionsAndView(appWidgetId, updatedTile); + } + deleteWidgets(new int[]{appWidgetId}); } } } - /** Checks the current state of {@code tile} dependencies, updating fields as necessary. */ + /** Checks the current state of {@code tile} dependencies, modifying fields as necessary. */ @Nullable - private PeopleSpaceTile updateWithCurrentState(PeopleSpaceTile tile, - String entryPoint) { + private PeopleSpaceTile getTileWithCurrentState(PeopleSpaceTile tile, + String entryPoint) throws + PackageManager.NameNotFoundException { PeopleSpaceTile.Builder updatedTile = tile.toBuilder(); - try { - switch (entryPoint) { - case NotificationManager - .ACTION_INTERRUPTION_FILTER_CHANGED: - updatedTile.setNotificationPolicyState(getNotificationPolicyState()); - break; - case Intent.ACTION_PACKAGES_SUSPENDED: - case Intent.ACTION_PACKAGES_UNSUSPENDED: - updatedTile.setIsPackageSuspended(getPackageSuspended(tile)); - break; - case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: - case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: - case Intent.ACTION_USER_UNLOCKED: - updatedTile.setIsUserQuieted(getUserQuieted(tile)); - break; - case Intent.ACTION_LOCALE_CHANGED: - break; - case ACTION_BOOT_COMPLETED: - default: - updatedTile.setIsUserQuieted(getUserQuieted(tile)).setIsPackageSuspended( - getPackageSuspended(tile)).setNotificationPolicyState( - getNotificationPolicyState()); - } - } catch (Exception e) { - Log.e(TAG, "Package no longer found for tile: " + tile.toString() + e); - return null; + switch (entryPoint) { + case NotificationManager + .ACTION_INTERRUPTION_FILTER_CHANGED: + updatedTile.setNotificationPolicyState(getNotificationPolicyState()); + break; + case Intent.ACTION_PACKAGES_SUSPENDED: + case Intent.ACTION_PACKAGES_UNSUSPENDED: + updatedTile.setIsPackageSuspended(getPackageSuspended(tile)); + break; + case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: + case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: + case Intent.ACTION_USER_UNLOCKED: + updatedTile.setIsUserQuieted(getUserQuieted(tile)); + break; + case Intent.ACTION_LOCALE_CHANGED: + break; + case ACTION_BOOT_COMPLETED: + default: + updatedTile.setIsUserQuieted(getUserQuieted(tile)).setIsPackageSuspended( + getPackageSuspended(tile)).setNotificationPolicyState( + getNotificationPolicyState()); } return updatedTile.build(); } - private boolean getPackageSuspended(PeopleSpaceTile tile) throws Exception { + private boolean getPackageSuspended(PeopleSpaceTile tile) throws + PackageManager.NameNotFoundException { boolean packageSuspended = !TextUtils.isEmpty(tile.getPackageName()) && mPackageManager.isPackageSuspended(tile.getPackageName()); if (DEBUG) Log.d(TAG, "Package suspended: " + packageSuspended); + // isPackageSuspended() only throws an exception if the app has been uninstalled, and the + // app data has also been cleared. We want to empty the layout when the app is uninstalled + // regardless of app data clearing, which getApplicationInfoAsUser() handles. + mPackageManager.getApplicationInfoAsUser( + tile.getPackageName(), PackageManager.GET_META_DATA, + PeopleSpaceUtils.getUserId(tile)); return packageSuspended; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java index f6d93895ce05..929aedae6706 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -28,6 +28,7 @@ import android.view.View; import android.widget.TextView; import android.widget.Toast; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto; @@ -73,7 +74,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme private final PageIndicator mPageIndicator; private final View mPowerMenuLite; private final boolean mShowPMLiteButton; - private GlobalActionsDialogLite mGlobalActionsDialog; + private final GlobalActionsDialogLite mGlobalActionsDialog; private final UiEventLogger mUiEventLogger; private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener = @@ -272,7 +273,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme private void startSettingsActivity() { ActivityLaunchAnimator.Controller animationController = mSettingsButtonContainer != null ? ActivityLaunchAnimator.Controller.fromView( - mSettingsButtonContainer) : null; + mSettingsButtonContainer, + InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON) : null; mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS), true /* dismissShade */, animationController); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index e1a66b2c07ee..7b8a6a0a8d0e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -539,8 +539,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) { View hostView = mediaHost.getHostView(); - // on keyguard we cross-fade to expanded, so no need to pin it. - if (mLastQSExpansion > 0 && !isKeyguardState()) { + // On keyguard we cross-fade to expanded, so no need to pin it. + // If the collapsed qs isn't visible, we also just keep it at the laid out position. + if (mLastQSExpansion > 0 && !isKeyguardState() && mQqsMediaHost.getVisible()) { float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView) - hostView.getHeight(); float currentPosition = mediaHost.getCurrentBounds().top diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 525bad8a0e25..6ddf2a75f491 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -45,7 +45,9 @@ import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.qs.external.CustomTile; +import com.android.systemui.qs.external.CustomTileStatePersister; import com.android.systemui.qs.external.TileLifecycleManager; +import com.android.systemui.qs.external.TileServiceKey; import com.android.systemui.qs.external.TileServices; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.UserTracker; @@ -93,6 +95,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D private final QSLogger mQSLogger; private final UiEventLogger mUiEventLogger; private final InstanceIdSequence mInstanceIdSequence; + private final CustomTileStatePersister mCustomTileStatePersister; private final List<Callback> mCallbacks = new ArrayList<>(); private AutoTileManager mAutoTiles; @@ -119,7 +122,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D QSLogger qsLogger, UiEventLogger uiEventLogger, UserTracker userTracker, - SecureSettings secureSettings) { + SecureSettings secureSettings, + CustomTileStatePersister customTileStatePersister) { mIconController = iconController; mContext = context; mUserContext = context; @@ -139,6 +143,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mDumpManager.registerDumpable(TAG, this); mUserTracker = userTracker; mSecureSettings = secureSettings; + mCustomTileStatePersister = customTileStatePersister; mainHandler.post(() -> { // This is technically a hack to avoid circular dependency of @@ -418,6 +423,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D changeTiles(mTileSpecs, newSpecs); } + /** + * Change the tiles triggered by the user editing. + * <p> + * This is not called on device start, or on user change. + */ public void changeTiles(List<String> previousTiles, List<String> newTiles) { final List<String> copy = new ArrayList<>(previousTiles); final int NP = copy.size(); @@ -433,6 +443,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mBroadcastDispatcher); lifecycleManager.onStopListening(); lifecycleManager.onTileRemoved(); + mCustomTileStatePersister.removeState(new TileServiceKey(component, mCurrentUser)); TileLifecycleManager.setTileAdded(mContext, component, false); lifecycleManager.flushMessagesAndUnbind(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index e24acf27c973..659475d19277 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -228,24 +228,5 @@ public class QuickQSPanel extends QSPanel { } } } - - @Override - public void setExpansion(float expansion) { - if (expansion > 0f && expansion < 1f) { - return; - } - boolean selected = expansion == 0f; - // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this - // point we want them to be selected so the tiles will marquee (but not at other points - // of expansion. - // We set it as not important while we change this, so setting each tile as selected - // will not cause them to announce themselves until the user has actually selected the - // item. - setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).setSelected(selected); - } - setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); - } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 7cc6ecd8cf62..997b96626747 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -399,12 +399,14 @@ public class QuickStatusBarHeader extends FrameLayout { mClockIconsSeparatorLayoutParams.width = 0; setSeparatorVisibility(false); mShowClockIconsSeparator = false; + mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE); } else { datePrivacySeparatorLayoutParams.width = topCutout.width(); mDatePrivacySeparator.setVisibility(View.VISIBLE); mClockIconsSeparatorLayoutParams.width = topCutout.width(); mShowClockIconsSeparator = true; setSeparatorVisibility(mKeyguardExpansionFraction == 0f); + mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON); } } mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams); diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 10eea828bcb4..396eca5c1bee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -46,6 +46,7 @@ import android.widget.Switch; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -85,6 +86,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener private final IQSTileService mService; private final TileServiceManager mServiceManager; private final int mUser; + private final CustomTileStatePersister mCustomTileStatePersister; private android.graphics.drawable.Icon mDefaultIcon; private CharSequence mDefaultLabel; @@ -94,6 +96,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener private boolean mIsTokenGranted; private boolean mIsShowingDialog; + private final TileServiceKey mKey; + private CustomTile( QSHost host, Looper backgroundLooper, @@ -104,7 +108,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener ActivityStarter activityStarter, QSLogger qsLogger, String action, - Context userContext + Context userContext, + CustomTileStatePersister customTileStatePersister ) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); @@ -113,15 +118,29 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener mTile = new Tile(); mUserContext = userContext; mUser = mUserContext.getUserId(); - updateDefaultTileAndIcon(); + mKey = new TileServiceKey(mComponent, mUser); + mServiceManager = host.getTileServices().getTileWrapper(this); + mService = mServiceManager.getTileService(); + mCustomTileStatePersister = customTileStatePersister; + } + + @Override + protected void handleInitialize() { + updateDefaultTileAndIcon(); if (mServiceManager.isToggleableTile()) { // Replace states with BooleanState resetStates(); } - - mService = mServiceManager.getTileService(); mServiceManager.setTileChangeListener(this); + if (mServiceManager.isActiveTile()) { + Tile t = mCustomTileStatePersister.readState(mKey); + if (t != null) { + applyTileState(t, /* overwriteNulls */ false); + mServiceManager.clearPendingBind(); + refreshState(); + } + } } @Override @@ -191,7 +210,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener @Override public void onTileChanged(ComponentName tile) { - updateDefaultTileAndIcon(); + mHandler.post(this::updateDefaultTileAndIcon); } @Override @@ -213,16 +232,44 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } public Tile getQsTile() { + // TODO(b/191145007) Move to background thread safely updateDefaultTileAndIcon(); return mTile; } - public void updateState(Tile tile) { - mTile.setIcon(tile.getIcon()); - mTile.setLabel(tile.getLabel()); - mTile.setSubtitle(tile.getSubtitle()); - mTile.setContentDescription(tile.getContentDescription()); - mTile.setStateDescription(tile.getStateDescription()); + /** + * Update state of {@link this#mTile} from a remote {@link TileService}. + * @param tile tile populated with state to apply + */ + public void updateTileState(Tile tile) { + // This comes from a binder call IQSService.updateQsTile + mHandler.post(() -> handleUpdateTileState(tile)); + } + + private void handleUpdateTileState(Tile tile) { + applyTileState(tile, /* overwriteNulls */ true); + if (mServiceManager.isActiveTile()) { + mCustomTileStatePersister.persistState(mKey, tile); + } + } + + @WorkerThread + private void applyTileState(Tile tile, boolean overwriteNulls) { + if (tile.getIcon() != null || overwriteNulls) { + mTile.setIcon(tile.getIcon()); + } + if (tile.getLabel() != null || overwriteNulls) { + mTile.setLabel(tile.getLabel()); + } + if (tile.getSubtitle() != null || overwriteNulls) { + mTile.setSubtitle(tile.getSubtitle()); + } + if (tile.getContentDescription() != null || overwriteNulls) { + mTile.setContentDescription(tile.getContentDescription()); + } + if (tile.getStateDescription() != null || overwriteNulls) { + mTile.setStateDescription(tile.getStateDescription()); + } mTile.setState(tile.getState()); } @@ -459,6 +506,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener final StatusBarStateController mStatusBarStateController; final ActivityStarter mActivityStarter; final QSLogger mQSLogger; + final CustomTileStatePersister mCustomTileStatePersister; Context mUserContext; String mSpec = ""; @@ -472,7 +520,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener MetricsLogger metricsLogger, StatusBarStateController statusBarStateController, ActivityStarter activityStarter, - QSLogger qsLogger + QSLogger qsLogger, + CustomTileStatePersister customTileStatePersister ) { mQSHostLazy = hostLazy; mBackgroundLooper = backgroundLooper; @@ -482,6 +531,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener mStatusBarStateController = statusBarStateController; mActivityStarter = activityStarter; mQSLogger = qsLogger; + mCustomTileStatePersister = customTileStatePersister; } Builder setSpec(@NonNull String spec) { @@ -509,7 +559,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener mActivityStarter, mQSLogger, action, - mUserContext + mUserContext, + mCustomTileStatePersister ); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt new file mode 100644 index 000000000000..021e632810f9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.external + +import android.content.ComponentName +import android.content.Context +import android.service.quicksettings.Tile +import android.util.Log +import com.android.internal.annotations.VisibleForTesting +import org.json.JSONException +import org.json.JSONObject +import javax.inject.Inject + +data class TileServiceKey(val componentName: ComponentName, val user: Int) { + private val string = "${componentName.flattenToString()}:$user" + override fun toString() = string +} +private const val STATE = "state" +private const val LABEL = "label" +private const val SUBTITLE = "subtitle" +private const val CONTENT_DESCRIPTION = "content_description" +private const val STATE_DESCRIPTION = "state_description" + +/** + * Persists and retrieves state for [CustomTile]. + * + * This class will persists to a fixed [SharedPreference] file a state for a pair of [ComponentName] + * and user id ([TileServiceKey]). + * + * It persists the state from a [Tile] necessary to present the view in the same state when + * retrieved, with the exception of the icon. + */ +class CustomTileStatePersister @Inject constructor(context: Context) { + companion object { + private const val FILE_NAME = "custom_tiles_state" + } + + private val sharedPreferences = context.getSharedPreferences(FILE_NAME, 0) + + /** + * Read the state from [SharedPreferences]. + * + * Returns `null` if the tile has no saved state. + * + * Any fields that have not been saved will be set to `null` + */ + fun readState(key: TileServiceKey): Tile? { + val state = sharedPreferences.getString(key.toString(), null) ?: return null + return try { + readTileFromString(state) + } catch (e: JSONException) { + Log.e("TileServicePersistence", "Bad saved state: $state", e) + null + } + } + + /** + * Persists the state into [SharedPreferences]. + * + * The implementation does not store fields that are `null` or icons. + */ + fun persistState(key: TileServiceKey, tile: Tile) { + val state = writeToString(tile) + + sharedPreferences.edit().putString(key.toString(), state).apply() + } + + /** + * Removes the state for a given tile, user pair. + * + * Used when the tile is removed by the user. + */ + fun removeState(key: TileServiceKey) { + sharedPreferences.edit().remove(key.toString()).apply() + } +} + +@VisibleForTesting +internal fun readTileFromString(stateString: String): Tile { + val json = JSONObject(stateString) + return Tile().apply { + state = json.getInt(STATE) + label = json.getStringOrNull(LABEL) + subtitle = json.getStringOrNull(SUBTITLE) + contentDescription = json.getStringOrNull(CONTENT_DESCRIPTION) + stateDescription = json.getStringOrNull(STATE_DESCRIPTION) + } +} + +// Properties with null values will not be saved to the Json string in any way. This makes sure +// to properly retrieve a null in that case. +private fun JSONObject.getStringOrNull(name: String): String? { + return if (has(name)) getString(name) else null +} + +@VisibleForTesting +internal fun writeToString(tile: Tile): String { + // Not storing the icon + return with(tile) { + JSONObject() + .put(STATE, state) + .put(LABEL, label) + .put(SUBTITLE, subtitle) + .put(CONTENT_DESCRIPTION, contentDescription) + .put(STATE_DESCRIPTION, stateDescription) + .toString() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java index 35cf2a12745e..a7cd11314d7e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -204,7 +204,7 @@ public class TileServices extends IQSService.Stub { tileServiceManager.clearPendingBind(); tileServiceManager.setLastUpdate(System.currentTimeMillis()); } - customTile.updateState(tile); + customTile.updateTileState(tile); customTile.refreshState(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 8f7c493417ec..842fd6c62d06 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -160,7 +160,8 @@ public class QSFactoryImpl implements QSFactory { public QSTile createTile(String tileSpec) { QSTileImpl tile = createTileInternal(tileSpec); if (tile != null) { - tile.handleStale(); // Tile was just created, must be stale. + tile.initialize(); + tile.postStale(); // Tile was just created, must be stale. } return tile; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index a938821a343f..4616be8f7937 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -50,6 +50,7 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.InstanceId; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; @@ -158,6 +159,15 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy */ abstract public int getMetricsCategory(); + /** + * Performs initialization of the tile + * + * Use this to perform initialization of the tile. Empty by default. + */ + protected void handleInitialize() { + + } + protected QSTileImpl( QSHost host, Looper backgroundLooper, @@ -346,6 +356,15 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy mHandler.sendEmptyMessage(H.DESTROY); } + /** + * Schedules initialization of the tile. + * + * Should be called upon creation of the tile, before performing other operations + */ + public void initialize() { + mHandler.sendEmptyMessage(H.INITIALIZE); + } + public TState getState() { return mState; } @@ -370,6 +389,13 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy } /** + * Posts a stale message to the background thread. + */ + public void postStale() { + mHandler.sendEmptyMessage(H.STALE); + } + + /** * Handles secondary click on the tile. * * Defaults to {@link QSTileImpl#handleClick} @@ -389,7 +415,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy */ protected void handleLongClick(@Nullable View view) { ActivityLaunchAnimator.Controller animationController = - view != null ? ActivityLaunchAnimator.Controller.fromView(view) : null; + view != null ? ActivityLaunchAnimator.Controller.fromView(view, + InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) : null; mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0, animationController); } @@ -580,6 +607,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy private static final int SET_LISTENING = 13; @VisibleForTesting protected static final int STALE = 14; + private static final int INITIALIZE = 15; @VisibleForTesting protected H(Looper looper) { @@ -638,6 +666,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy } else if (msg.what == STALE) { name = "handleStale"; handleStale(); + } else if (msg.what == INITIALIZE) { + name = "initialize"; + handleInitialize(); } else { throw new IllegalArgumentException("Unknown msg: " + msg.what); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 8aed0a88fa35..d31e67c55777 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -190,6 +190,9 @@ open class QSTileViewImpl @JvmOverloads constructor( if (collapsed) { labelContainer.ignoreLastView = true secondaryLabel.alpha = 0f + // Do not marque in QQS + label.ellipsize = TextUtils.TruncateAt.END + secondaryLabel.ellipsize = TextUtils.TruncateAt.END } setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE)) setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE)) @@ -492,11 +495,7 @@ open class QSTileViewImpl @JvmOverloads constructor( } return if (state.state == Tile.STATE_UNAVAILABLE || state is BooleanState) { - val resName = "$TILE_STATE_RES_PREFIX${state.spec}" - var arrayResId = resources.getIdentifier(resName, "array", context.packageName) - if (arrayResId == 0) { - arrayResId = R.array.tile_states_default - } + var arrayResId = SubtitleArrayMapping.getSubtitleId(state.spec) val array = resources.getStringArray(arrayResId) array[state.state] } else { @@ -557,6 +556,41 @@ open class QSTileViewImpl @JvmOverloads constructor( private fun getChevronColorForState(state: Int): Int = getSecondaryLabelColorForState(state) } +@VisibleForTesting +internal object SubtitleArrayMapping { + private val subtitleIdsMap = mapOf<String?, Int>( + "internet" to R.array.tile_states_internet, + "wifi" to R.array.tile_states_wifi, + "cell" to R.array.tile_states_cell, + "battery" to R.array.tile_states_battery, + "dnd" to R.array.tile_states_dnd, + "flashlight" to R.array.tile_states_flashlight, + "rotation" to R.array.tile_states_rotation, + "bt" to R.array.tile_states_bt, + "airplane" to R.array.tile_states_airplane, + "location" to R.array.tile_states_location, + "hotspot" to R.array.tile_states_hotspot, + "inversion" to R.array.tile_states_inversion, + "saver" to R.array.tile_states_saver, + "dark" to R.array.tile_states_dark, + "work" to R.array.tile_states_work, + "cast" to R.array.tile_states_cast, + "night" to R.array.tile_states_night, + "screenrecord" to R.array.tile_states_screenrecord, + "reverse" to R.array.tile_states_reverse, + "reduce_brightness" to R.array.tile_states_reduce_brightness, + "cameratoggle" to R.array.tile_states_cameratoggle, + "mictoggle" to R.array.tile_states_mictoggle, + "controls" to R.array.tile_states_controls, + "wallet" to R.array.tile_states_wallet, + "alarm" to R.array.tile_states_alarm + ) + + fun getSubtitleId(spec: String?): Int { + return subtitleIdsMap.getOrDefault(spec, R.array.tile_states_default) + } +} + private fun colorValuesHolder(name: String, vararg values: Int): PropertyValuesHolder { return PropertyValuesHolder.ofInt(name, *values).apply { setEvaluator(ArgbEvaluator.getInstance()) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt index 69d49d44f822..73d13700d61b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt @@ -11,6 +11,7 @@ import android.text.TextUtils import android.text.format.DateFormat import android.view.View import androidx.annotation.VisibleForTesting +import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.MetricsLogger import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator @@ -70,7 +71,10 @@ class AlarmTile @Inject constructor( } override fun handleClick(view: View?) { - val animationController = view?.let { ActivityLaunchAnimator.Controller.fromView(it) } + val animationController = view?.let { + ActivityLaunchAnimator.Controller.fromView( + it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) + } val pendingIntent = lastAlarmInfo?.showIntent if (pendingIntent != null) { mActivityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt index 6d3190ffa725..f66b7226fbae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt @@ -22,6 +22,7 @@ import android.os.Handler import android.os.Looper import android.service.quicksettings.Tile import android.view.View +import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.MetricsLogger import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator @@ -106,7 +107,8 @@ class DeviceControlsTile @Inject constructor( putExtra(ControlsUiController.EXTRA_ANIMATE, true) } val animationController = view?.let { - ActivityLaunchAnimator.Controller.fromView(it) + ActivityLaunchAnimator.Controller.fromView( + it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) } mUiHandler.post { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 0e4434baa0e8..98cd88af232f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; @@ -120,7 +121,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override protected void handleClick(@Nullable View view) { ActivityLaunchAnimator.Controller animationController = - view == null ? null : ActivityLaunchAnimator.Controller.fromView(view); + view == null ? null : ActivityLaunchAnimator.Controller.fromView(view, + InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE); mUiHandler.post(() -> { if (mSelectedCard != null) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index 9e11451afa06..0a60f6da159e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -62,6 +62,7 @@ public class CropView extends View { private final float mCropTouchMargin; private final Paint mShadePaint; private final Paint mHandlePaint; + private final Paint mContainerBackgroundPaint; // Crop rect with each element represented as [0,1] along its proper axis. private RectF mCrop = new RectF(0, 0, 1, 1); @@ -79,6 +80,9 @@ public class CropView extends View { // The allowable values for the current boundary being dragged private Range<Float> mMotionRange; + // Value [0,1] indicating progress in animateEntrance() + private float mEntranceInterpolation = 1f; + private CropInteractionListener mCropInteractionListener; private final ExploreByTouchHelper mExploreByTouchHelper; @@ -92,6 +96,9 @@ public class CropView extends View { attrs, R.styleable.CropView, 0, 0); mShadePaint = new Paint(); mShadePaint.setColor(t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT)); + mContainerBackgroundPaint = new Paint(); + mContainerBackgroundPaint.setColor(t.getColor(R.styleable.CropView_containerBackgroundColor, + Color.TRANSPARENT)); mHandlePaint = new Paint(); mHandlePaint.setColor(t.getColor(R.styleable.CropView_handleColor, Color.BLACK)); mHandlePaint.setStrokeCap(Paint.Cap.ROUND); @@ -125,10 +132,22 @@ public class CropView extends View { @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - drawShade(canvas, 0, 0, 1, mCrop.top); - drawShade(canvas, 0, mCrop.bottom, 1, 1); + // Top and bottom borders reflect the boundary between the (scrimmed) image and the + // opaque container background. This is only meaningful during an entrance transition. + float topBorder = MathUtils.lerp(mCrop.top, 0, mEntranceInterpolation); + float bottomBorder = MathUtils.lerp(mCrop.bottom, 1, mEntranceInterpolation); + drawShade(canvas, 0, topBorder, 1, mCrop.top); + drawShade(canvas, 0, mCrop.bottom, 1, bottomBorder); drawShade(canvas, 0, mCrop.top, mCrop.left, mCrop.bottom); drawShade(canvas, mCrop.right, mCrop.top, 1, mCrop.bottom); + + // Entrance transition expects the crop bounds to be full width, so we only draw container + // background on the top and bottom. + drawContainerBackground(canvas, 0, 0, 1, topBorder); + drawContainerBackground(canvas, 0, bottomBorder, 1, 1); + + mHandlePaint.setAlpha((int) (mEntranceInterpolation * 255)); + drawHorizontalHandle(canvas, mCrop.top, /* draw the handle tab up */ true); drawHorizontalHandle(canvas, mCrop.bottom, /* draw the handle tab down */ false); drawVerticalHandle(canvas, mCrop.left, /* left */ true); @@ -282,6 +301,22 @@ public class CropView extends View { } /** + * Fade in crop bounds, animate reveal of cropped-out area from current crop bounds. + */ + public void animateEntrance() { + mEntranceInterpolation = 0; + ValueAnimator animator = new ValueAnimator(); + animator.addUpdateListener(animation -> { + mEntranceInterpolation = animation.getAnimatedFraction(); + invalidate(); + }); + animator.setFloatValues(0f, 1f); + animator.setDuration(750); + animator.setInterpolator(new FastOutSlowInInterpolator()); + animator.start(); + } + + /** * Set additional top and bottom padding for the image being cropped (used when the * corresponding ImageView doesn't take the full height). */ @@ -369,6 +404,13 @@ public class CropView extends View { fractionToVerticalPixels(bottom), mShadePaint); } + private void drawContainerBackground(Canvas canvas, float left, float top, float right, + float bottom) { + canvas.drawRect(fractionToHorizontalPixels(left), fractionToVerticalPixels(top), + fractionToHorizontalPixels(right), + fractionToVerticalPixels(bottom), mContainerBackgroundPaint); + } + private void drawHorizontalHandle(Canvas canvas, float frac, boolean handleTabUp) { int y = fractionToVerticalPixels(frac); canvas.drawLine(fractionToHorizontalPixels(mCrop.left), y, diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index d5b4032b1c0f..f571e417c636 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -32,8 +32,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; import android.text.TextUtils; -import android.transition.Transition; -import android.transition.TransitionListenerAdapter; import android.util.Log; import android.view.ScrollCaptureResponse; import android.view.View; @@ -74,7 +72,7 @@ public class LongScreenshotActivity extends Activity { private final Executor mUiExecutor; private final Executor mBackgroundExecutor; private final ImageExporter mImageExporter; - private final LongScreenshotHolder mLongScreenshotHolder; + private final LongScreenshotData mLongScreenshotHolder; private ImageView mPreview; private ImageView mTransitionView; @@ -103,7 +101,7 @@ public class LongScreenshotActivity extends Activity { @Inject public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter, @Main Executor mainExecutor, @Background Executor bgExecutor, - LongScreenshotHolder longScreenshotHolder) { + LongScreenshotData longScreenshotHolder) { mUiEventLogger = uiEventLogger; mUiExecutor = mainExecutor; mBackgroundExecutor = bgExecutor; @@ -116,7 +114,6 @@ public class LongScreenshotActivity extends Activity { public void onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")"); super.onCreate(savedInstanceState); - postponeEnterTransition(); setContentView(R.layout.long_screenshot); mPreview = requireViewById(R.id.preview); @@ -192,7 +189,6 @@ public class LongScreenshotActivity extends Activity { mLongScreenshot = longScreenshot; Drawable drawable = mLongScreenshot.getDrawable(); mPreview.setImageDrawable(drawable); - mCropView.setVisibility(View.VISIBLE); mMagnifierView.setDrawable(mLongScreenshot.getDrawable(), mLongScreenshot.getWidth(), mLongScreenshot.getHeight()); // Original boundaries go from the image tile set's y=0 to y=pageSize, so @@ -211,23 +207,22 @@ public class LongScreenshotActivity extends Activity { public boolean onPreDraw() { mEnterTransitionView.getViewTreeObserver().removeOnPreDrawListener(this); updateImageDimensions(); - startPostponedEnterTransition(); - if (isActivityTransitionRunning()) { - getWindow().getSharedElementEnterTransition().addListener( - new TransitionListenerAdapter() { - @Override - public void onTransitionEnd(Transition transition) { - super.onTransitionEnd(transition); - mPreview.animate().alpha(1f); - mCropView.animateBoundaryTo( - CropView.CropBoundary.TOP, topFraction); - mCropView.animateBoundaryTo( - CropView.CropBoundary.BOTTOM, bottomFraction); - setButtonsEnabled(true); - mEnterTransitionView.setVisibility(View.GONE); - } + mEnterTransitionView.post(() -> { + Rect dest = new Rect(); + mEnterTransitionView.getBoundsOnScreen(dest); + mLongScreenshotHolder.takeTransitionDestinationCallback() + .setTransitionDestination(dest, () -> { + mPreview.animate().alpha(1f); + mCropView.setBoundaryPosition( + CropView.CropBoundary.TOP, topFraction); + mCropView.setBoundaryPosition( + CropView.CropBoundary.BOTTOM, bottomFraction); + mCropView.animateEntrance(); + mCropView.setVisibility(View.VISIBLE); + setButtonsEnabled(true); + mEnterTransitionView.setVisibility(View.GONE); }); - } + }); return true; } }); @@ -250,6 +245,7 @@ public class LongScreenshotActivity extends Activity { Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")"); BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap); mPreview.setImageDrawable(drawable); + mPreview.setAlpha(1f); mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(), imageResult.bitmap.getHeight()); mCropView.setVisibility(View.VISIBLE); @@ -476,19 +472,21 @@ public class LongScreenshotActivity extends Activity { params.height = boundaries.height(); mTransitionView.setLayoutParams(params); - ConstraintLayout.LayoutParams enterTransitionParams = - (ConstraintLayout.LayoutParams) mEnterTransitionView.getLayoutParams(); - float topFraction = Math.max(0, - -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight()); - enterTransitionParams.width = (int) (scale * drawable.getIntrinsicWidth()); - enterTransitionParams.height = (int) (scale * mLongScreenshot.getPageHeight()); - mEnterTransitionView.setLayoutParams(enterTransitionParams); - - Matrix matrix = new Matrix(); - matrix.setScale(scale, scale); - matrix.postTranslate(0, -scale * drawable.getIntrinsicHeight() * topFraction); - mEnterTransitionView.setImageMatrix(matrix); - mEnterTransitionView.setTranslationY( - topFraction * previewHeight + mPreview.getPaddingTop() + extraPadding); + if (mLongScreenshot != null) { + ConstraintLayout.LayoutParams enterTransitionParams = + (ConstraintLayout.LayoutParams) mEnterTransitionView.getLayoutParams(); + float topFraction = Math.max(0, + -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight()); + enterTransitionParams.width = (int) (scale * drawable.getIntrinsicWidth()); + enterTransitionParams.height = (int) (scale * mLongScreenshot.getPageHeight()); + mEnterTransitionView.setLayoutParams(enterTransitionParams); + + Matrix matrix = new Matrix(); + matrix.setScale(scale, scale); + matrix.postTranslate(0, -scale * drawable.getIntrinsicHeight() * topFraction); + mEnterTransitionView.setImageMatrix(matrix); + mEnterTransitionView.setTranslationY( + topFraction * previewHeight + mPreview.getPaddingTop() + extraPadding); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java index 39c6f0798988..f549faf2414a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java @@ -23,16 +23,19 @@ import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; /** - * LongScreenshotHolder holds on to 1 LongScreenshot reference to facilitate indirect in-process - * passing. + * LongScreenshotData holds on to 1 LongScreenshot reference and 1 TransitionDestination + * reference, to facilitate indirect in-process passing. */ @SysUISingleton -public class LongScreenshotHolder { +public class LongScreenshotData { private final AtomicReference<ScrollCaptureController.LongScreenshot> mLongScreenshot; + private final AtomicReference<ScreenshotController.TransitionDestination> + mTransitionDestinationCallback; @Inject - public LongScreenshotHolder() { + public LongScreenshotData() { mLongScreenshot = new AtomicReference<>(); + mTransitionDestinationCallback = new AtomicReference<>(); } /** @@ -56,4 +59,18 @@ public class LongScreenshotHolder { return mLongScreenshot.getAndSet(null); } + /** + * Set the holder's TransitionDestination callback. + */ + public void setTransitionDestinationCallback( + ScreenshotController.TransitionDestination destination) { + mTransitionDestinationCallback.set(destination); + } + + /** + * Return the current TransitionDestination callback and clear. + */ + public ScreenshotController.TransitionDestination takeTransitionDestinationCallback() { + return mTransitionDestinationCallback.getAndSet(null); + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 5efa1b2992b2..2e138362aedb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -64,6 +64,7 @@ import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.ScrollCaptureResponse; import android.view.SurfaceControl; @@ -72,6 +73,7 @@ import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; @@ -203,6 +205,15 @@ public class ScreenshotController { void onActionsReady(ScreenshotController.QuickShareData quickShareData); } + interface TransitionDestination { + /** + * Allows the long screenshot activity to call back with a destination location (the bounds + * on screen of the destination for the transitioning view) and a Runnable to be run once + * the transition animation is complete. + */ + void setTransitionDestination(Rect transitionDestination, Runnable onTransitionEnd); + } + // These strings are used for communicating the action invoked to // ScreenshotNotificationSmartActionsProvider. static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type"; @@ -241,7 +252,7 @@ public class ScreenshotController { private final PhoneWindow mWindow; private final DisplayManager mDisplayManager; private final ScrollCaptureController mScrollCaptureController; - private final LongScreenshotHolder mLongScreenshotHolder; + private final LongScreenshotData mLongScreenshotHolder; private ScreenshotView mScreenshotView; private Bitmap mScreenBitmap; @@ -286,7 +297,7 @@ public class ScreenshotController { ImageExporter imageExporter, @Main Executor mainExecutor, ScrollCaptureController scrollCaptureController, - LongScreenshotHolder longScreenshotHolder) { + LongScreenshotData longScreenshotHolder) { mScreenshotSmartActions = screenshotSmartActions; mNotificationsController = screenshotNotificationsController; mScrollCaptureClient = scrollCaptureClient; @@ -573,6 +584,10 @@ public class ScreenshotController { mScreenshotView.updateDisplayCutoutMargins( mWindowManager.getCurrentWindowMetrics().getWindowInsets() .getDisplayCutout()); + // screenshot animation calculations won't be valid anymore, so just end + if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { + mScreenshotAnimation.end(); + } } }); }); @@ -644,17 +659,29 @@ public class ScreenshotController { } mLongScreenshotHolder.setLongScreenshot(longScreenshot); + mLongScreenshotHolder.setTransitionDestinationCallback( + (transitionDestination, onTransitionEnd) -> + mScreenshotView.startLongScreenshotTransition( + transitionDestination, onTransitionEnd, + longScreenshot)); final Intent intent = new Intent(mContext, LongScreenshotActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); Pair<ActivityOptions, ExitTransitionCoordinator> transition = - ActivityOptions.startSharedElementAnimation( - mWindow, new ScreenshotExitTransitionCallbacks(), null, - Pair.create(mScreenshotView.getScrollablePreview(), - ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); + ActivityOptions.startSharedElementAnimation(mWindow, + new ScreenshotExitTransitionCallbacksSupplier(false).get(), + null); transition.second.startExit(); mContext.startActivity(intent, transition.first.toBundle()); + RemoteAnimationAdapter runner = new RemoteAnimationAdapter( + SCREENSHOT_REMOTE_RUNNER, 0, 0); + try { + WindowManagerGlobal.getWindowManagerService() + .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY); + } catch (Exception e) { + Log.e(TAG, "Error overriding screenshot app transition", e); + } }, mMainExecutor); }); } catch (CancellationException e) { @@ -879,8 +906,8 @@ public class ScreenshotController { return () -> { Pair<ActivityOptions, ExitTransitionCoordinator> transition = ActivityOptions.startSharedElementAnimation( - mWindow, new ScreenshotExitTransitionCallbacks(), null, - Pair.create(mScreenshotView.getScreenshotPreview(), + mWindow, new ScreenshotExitTransitionCallbacksSupplier(true).get(), + null, Pair.create(mScreenshotView.getScreenshotPreview(), ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); transition.second.startExit(); @@ -966,19 +993,33 @@ public class ScreenshotController { return matchWithinTolerance; } - private class ScreenshotExitTransitionCallbacks implements ExitTransitionCallbacks { - @Override - public boolean isReturnTransitionAllowed() { - return false; - } + private class ScreenshotExitTransitionCallbacksSupplier implements + Supplier<ExitTransitionCallbacks> { + final boolean mDismissOnHideSharedElements; - @Override - public void hideSharedElements() { - finishDismiss(); + ScreenshotExitTransitionCallbacksSupplier(boolean dismissOnHideSharedElements) { + mDismissOnHideSharedElements = dismissOnHideSharedElements; } @Override - public void onFinish() { + public ExitTransitionCallbacks get() { + return new ExitTransitionCallbacks() { + @Override + public boolean isReturnTransitionAllowed() { + return false; + } + + @Override + public void hideSharedElements() { + if (mDismissOnHideSharedElements) { + finishDismiss(); + } + } + + @Override + public void onFinish() { + } + }; } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 2a2217912b58..669046591170 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -479,6 +479,11 @@ public class ScreenshotView extends FrameLayout implements final PointF finalPos = new PointF(targetPosition.exactCenterX(), targetPosition.exactCenterY()); + // Shift to screen coordinates so that the animation runs on top of the entire screen, + // including e.g. bars covering the display cutout. + int[] locInScreen = mScreenshotPreview.getLocationOnScreen(); + startPos.offset(targetPosition.left - locInScreen[0], targetPosition.top - locInScreen[1]); + if (DEBUG_ANIM) { Log.d(TAG, "toCorner: startPos=" + startPos); Log.d(TAG, "toCorner: finalPos=" + finalPos); @@ -759,7 +764,53 @@ public class ScreenshotView extends FrameLayout implements return r; } + void startLongScreenshotTransition(Rect destination, Runnable onTransitionEnd, + ScrollCaptureController.LongScreenshot longScreenshot) { + mScrollablePreview.setImageBitmap(longScreenshot.toBitmap()); + ValueAnimator anim = ValueAnimator.ofFloat(0, 1); + float startX = mScrollablePreview.getX(); + float startY = mScrollablePreview.getY(); + int[] locInScreen = mScrollablePreview.getLocationOnScreen(); + destination.offset((int) startX - locInScreen[0], (int) startY - locInScreen[1]); + mScrollablePreview.setPivotX(0); + mScrollablePreview.setPivotY(0); + mScrollablePreview.setAlpha(1f); + float currentScale = mScrollablePreview.getWidth() / (float) longScreenshot.getWidth(); + Matrix matrix = new Matrix(); + matrix.setScale(currentScale, currentScale); + matrix.postTranslate( + longScreenshot.getLeft() * currentScale, longScreenshot.getTop() * currentScale); + mScrollablePreview.setImageMatrix(matrix); + float destinationScale = destination.width() / (float) mScrollablePreview.getWidth(); + anim.addUpdateListener(animation -> { + float t = animation.getAnimatedFraction(); + mScrollingScrim.setAlpha(1 - t); + float currScale = MathUtils.lerp(1, destinationScale, t); + mScrollablePreview.setScaleX(currScale); + mScrollablePreview.setScaleY(currScale); + mScrollablePreview.setX(MathUtils.lerp(startX, destination.left, t)); + mScrollablePreview.setY(MathUtils.lerp(startY, destination.top, t)); + }); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + onTransitionEnd.run(); + mScrollablePreview.animate().alpha(0).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + mCallbacks.onDismiss(); + } + }); + } + }); + anim.start(); + } + void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap) { + mScrollingScrim.setImageBitmap(screenBitmap); + mScrollingScrim.setVisibility(View.VISIBLE); Rect scrollableArea = scrollableAreaOnScreen(response); float scale = mCornerSizeX / (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight()); @@ -770,14 +821,12 @@ public class ScreenshotView extends FrameLayout implements params.height = (int) (scale * scrollableArea.height()); Matrix matrix = new Matrix(); matrix.setScale(scale, scale); - matrix.postTranslate(0, -scrollableArea.top * scale); + matrix.postTranslate(-scrollableArea.left * scale, -scrollableArea.top * scale); mScrollablePreview.setTranslationX(scale * scrollableArea.left); mScrollablePreview.setTranslationY(scale * scrollableArea.top); mScrollablePreview.setImageMatrix(matrix); - mScrollingScrim.setImageBitmap(screenBitmap); - mScrollingScrim.setVisibility(View.VISIBLE); mScrollablePreview.setImageBitmap(screenBitmap); mScrollablePreview.setVisibility(View.VISIBLE); createScreenshotFadeDismissAnimation(true).start(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 91a0e6fedef8..0bb702f6c9e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -112,6 +112,7 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private ViewGroup mIndicationArea; private KeyguardIndicationTextView mTopIndicationView; + private KeyguardIndicationTextView mLockScreenIndicationView; private final IBatteryStats mBatteryInfo; private final SettableWakeLock mWakeLock; private final DockManager mDockManager; @@ -208,17 +209,21 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal mKeyguardUpdateMonitor.registerCallback(mTickReceiver); mStatusBarStateController.addCallback(mStatusBarStateListener); mKeyguardStateController.addCallback(this); + + mStatusBarStateListener.onDozingChanged(mStatusBarStateController.isDozing()); } public void setIndicationArea(ViewGroup indicationArea) { mIndicationArea = indicationArea; mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text); + mLockScreenIndicationView = indicationArea.findViewById( + R.id.keyguard_indication_text_bottom); mInitialTextColorState = mTopIndicationView != null ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE); mRotateTextViewController = new KeyguardIndicationRotateTextViewController( - indicationArea.findViewById(R.id.keyguard_indication_text_bottom), - mExecutor, - mStatusBarStateController); + mLockScreenIndicationView, + mExecutor, + mStatusBarStateController); updateIndication(false /* animate */); updateDisclosure(); if (mBroadcastReceiver == null) { @@ -630,6 +635,7 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal // should be shown based on user or device state // AoD if (mDozing) { + mLockScreenIndicationView.setVisibility(View.GONE); mTopIndicationView.setVisibility(VISIBLE); // When dozing we ignore any text color and use white instead, because // colors can be hard to read in low brightness. @@ -659,6 +665,8 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal // LOCK SCREEN mTopIndicationView.setVisibility(GONE); + mTopIndicationView.setText(null); + mLockScreenIndicationView.setVisibility(View.VISIBLE); updateIndications(animate, KeyguardUpdateMonitor.getCurrentUser()); } @@ -914,7 +922,8 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal } else if (mStatusBarKeyguardViewManager.isBouncerShowing()) { mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState); } else if (mKeyguardUpdateMonitor.isScreenOn()) { - showTransientIndication(errString); + showTransientIndication(errString, /* isError */ true, + /* hideOnScreenOff */ true); // We want to keep this message around in case the screen was off hideTransientIndicationDelayed(HIDE_DELAY_MS); } else { @@ -1032,9 +1041,8 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal if (mHideTransientMessageOnScreenOff && mDozing) { hideTransientIndication(); - } else { - updateIndication(false); } + updateIndication(false); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 6a5f001ac2ee..ec648ad519a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -93,10 +93,10 @@ class CircleReveal( val endRadius: Float ) : LightRevealEffect { override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) { - val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(amount) - val fadeAmount = - LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.75f) - val radius = startRadius + ((endRadius - startRadius) * interpolatedAmount) + // reveal amount updates already have an interpolator, so we intentionally use the + // non-interpolated amount + val fadeAmount = LightRevealEffect.getPercentPastThreshold(amount, 0.5f) + val radius = startRadius + ((endRadius - startRadius) * amount) scrim.revealGradientEndColorAlpha = 1f - fadeAmount scrim.setRevealGradientBounds( centerX - radius /* left */, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index a69b8d60681c..25cbdc5c2187 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -245,7 +245,8 @@ public class NotificationMediaManager implements Dumpable { mMediaDataManager.addListener(new MediaDataManager.Listener() { @Override public void onMediaDataLoaded(@NonNull String key, - @Nullable String oldKey, @NonNull MediaData data, boolean immediately) { + @Nullable String oldKey, @NonNull MediaData data, boolean immediately, + boolean isSsReactivated) { } @Override @@ -318,7 +319,8 @@ public class NotificationMediaManager implements Dumpable { mMediaDataManager.addListener(new MediaDataManager.Listener() { @Override public void onMediaDataLoaded(@NonNull String key, - @Nullable String oldKey, @NonNull MediaData data, boolean immediately) { + @Nullable String oldKey, @NonNull MediaData data, boolean immediately, + boolean isSsReactivated) { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 9f59023f1890..f8a1ff879e72 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -145,11 +145,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll } @Override - public boolean setState(int state) { + public boolean setState(int state, boolean force) { if (state > MAX_STATE || state < MIN_STATE) { throw new IllegalArgumentException("Invalid state " + state); } - if (state == mState) { + if (!force && state == mState) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java index b6d6ed53b681..73f3d90bd4f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -59,7 +59,19 @@ public interface SysuiStatusBarStateController extends StatusBarStateController * @param state see {@link StatusBarState} for valid options * @return {@code true} if the state changed, else {@code false} */ - boolean setState(int state); + default boolean setState(int state) { + return setState(state, false /* force */); + } + + /** + * Update the status bar state + * @param state see {@link StatusBarState} for valid options + * @param force whether to set the state even if it's the same as the current state. This will + * dispatch the state to all StatusBarStateListeners, ensuring that all listening + * components are reset to this state. + * @return {@code true} if the state was changed or set forcefully + */ + boolean setState(int state, boolean force); /** * Update the dozing state from {@link StatusBar}'s perspective diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index 8479b30c3a75..f30010cf4d1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -383,10 +383,18 @@ const val ANIMATING_OUT = 3 const val SHOWING_PERSISTENT_DOT = 4 private const val TAG = "SystemStatusAnimationScheduler" -private const val DELAY: Long = 100 -private const val DISPLAY_LENGTH = 5000L -private const val ENTRANCE_ANIM_LENGTH = 500L -private const val CHIP_ANIM_LENGTH = 500L +private const val DELAY = 0L + +/** + * The total time spent animation should be 1500ms. The entrance animation is how much time + * we give to the system to animate system elements out of the way. Total chip animation length + * will be equivalent to 2*chip_anim_length + display_length + */ +private const val ENTRANCE_ANIM_LENGTH = 250L +private const val CHIP_ANIM_LENGTH = 250L +// 1s + entrance time + chip anim_length +private const val DISPLAY_LENGTH = 1500L + private const val MIN_UPTIME: Long = 5 * 1000 private const val DEBUG = false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java index aef01e9bd811..0fb1c54bb150 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java @@ -71,11 +71,11 @@ public final class NotificationClicker implements View.OnClickListener { // Check if the notification is displaying the menu, if so slide notification back if (isMenuVisible(row)) { mLogger.logMenuVisible(entry); - row.animateTranslateNotification(0); + row.animateResetTranslation(); return; } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) { mLogger.logParentMenuVisible(entry); - row.getNotificationParent().animateTranslateNotification(0); + row.getNotificationParent().animateResetTranslation(); return; } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) { // We never want to open the app directly if the user clicks in between diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 760bee21b0d1..b0a7767accfc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -264,7 +264,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } override fun onStateChanged(newState: Int) { - if (unlockedScreenOffAnimationController.shouldPlayScreenOffAnimation()) { + if (dozeParameters.shouldControlUnlockedScreenOff()) { if (unlockedScreenOffAnimationController.isScreenOffAnimationPlaying() && state == StatusBarState.KEYGUARD && newState == StatusBarState.SHADE) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 413662447028..4f3406c405ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -203,6 +203,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg); } + protected boolean hideBackground() { + return false; + } + + protected void updateBackground() { + mBackgroundNormal.setVisibility(hideBackground() ? INVISIBLE : VISIBLE); + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { @@ -330,30 +338,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } - @Override - public void setDistanceToTopRoundness(float distanceToTopRoundness) { - super.setDistanceToTopRoundness(distanceToTopRoundness); - mBackgroundNormal.setDistanceToTopRoundness(distanceToTopRoundness); - } - - /** Sets whether this view is the last notification in a section. */ - @Override - public void setLastInSection(boolean lastInSection) { - if (lastInSection != mLastInSection) { - super.setLastInSection(lastInSection); - mBackgroundNormal.setLastInSection(lastInSection); - } - } - - /** Sets whether this view is the first notification in a section. */ - @Override - public void setFirstInSection(boolean firstInSection) { - if (firstInSection != mFirstInSection) { - super.setFirstInSection(firstInSection); - mBackgroundNormal.setFirstInSection(firstInSection); - } - } - /** * Set an override tint color that is used for the background. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 6fd556763943..9d56e9b6f855 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -846,8 +846,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView updateClickAndFocus(); if (mNotificationParent != null) { setOverrideTintColor(NO_COLOR, 0.0f); - // Let's reset the distance to top roundness, as this isn't applied to group children - setDistanceToTopRoundness(NO_ROUNDNESS); mNotificationParent.updateBackgroundForGroupState(); } updateBackgroundClipping(); @@ -876,7 +874,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override protected boolean handleSlideBack() { if (mMenuRow != null && mMenuRow.isMenuVisible()) { - animateTranslateNotification(0 /* targetLeft */); + animateResetTranslation(); return true; } return false; @@ -1713,21 +1711,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this); mChildrenContainer.onNotificationUpdated(); - if (mShouldTranslateContents) { - mTranslateableViews.add(mChildrenContainer); - } + mTranslateableViews.add(mChildrenContainer); }); - if (mShouldTranslateContents) { - // Add the views that we translate to reveal the menu - mTranslateableViews = new ArrayList<>(); - for (int i = 0; i < getChildCount(); i++) { - mTranslateableViews.add(getChildAt(i)); - } - // Remove views that don't translate - mTranslateableViews.remove(mChildrenContainerStub); - mTranslateableViews.remove(mGutsStub); + // Add the views that we translate to reveal the menu + mTranslateableViews = new ArrayList<>(); + for (int i = 0; i < getChildCount(); i++) { + mTranslateableViews.add(getChildAt(i)); } + // Remove views that don't translate + mTranslateableViews.remove(mChildrenContainerStub); + mTranslateableViews.remove(mGutsStub); } private void doLongClickCallback() { @@ -1805,7 +1799,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mTranslateAnim.cancel(); } - if (!mShouldTranslateContents) { + if (mDismissUsingRowTranslationX) { setTranslationX(0); } else if (mTranslateableViews != null) { for (int i = 0; i < mTranslateableViews.size(); i++) { @@ -1867,23 +1861,47 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mPrivateLayout.getActiveRemoteInputText(); } - public void animateTranslateNotification(final float leftTarget) { + /** + * Reset the translation with an animation. + */ + public void animateResetTranslation() { if (mTranslateAnim != null) { mTranslateAnim.cancel(); } - mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */); + mTranslateAnim = getTranslateViewAnimator(0, null /* updateListener */); if (mTranslateAnim != null) { mTranslateAnim.start(); } } + /** + * Set the dismiss behavior of the view. + * @param usingRowTranslationX {@code true} if the view should translate using regular + * translationX, otherwise the contents will be + * translated. + */ + @Override + public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) { + if (usingRowTranslationX != mDismissUsingRowTranslationX) { + // In case we were already transitioning, let's switch over! + float previousTranslation = getTranslation(); + if (previousTranslation != 0) { + setTranslation(0); + } + super.setDismissUsingRowTranslationX(usingRowTranslationX); + if (previousTranslation != 0) { + setTranslation(previousTranslation); + } + } + } + @Override public void setTranslation(float translationX) { invalidate(); if (isBlockingHelperShowingAndTranslationFinished()) { mGuts.setTranslationX(translationX); return; - } else if (!mShouldTranslateContents) { + } else if (mDismissUsingRowTranslationX) { setTranslationX(translationX); } else if (mTranslateableViews != null) { // Translate the group of views @@ -1907,7 +1925,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public float getTranslation() { - if (!mShouldTranslateContents) { + if (mDismissUsingRowTranslationX) { return getTranslationX(); } @@ -2872,6 +2890,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mShowNoBackground = false; } updateOutline(); + updateBackground(); + } + + @Override + protected boolean hideBackground() { + return mShowNoBackground || super.hideBackground(); } public int getPositionOfChild(ExpandableNotificationRow childRow) { @@ -2898,7 +2922,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView float y = event.getY(); NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper(); NotificationHeaderView header = wrapper == null ? null : wrapper.getNotificationHeader(); - if (header != null && header.isInTouchRect(x - getTranslation(), y)) { + // the extra translation only needs to be added, if we're translating the notification + // contents, otherwise the motionEvent is already at the right place due to the + // touch event system. + float translation = !mDismissUsingRowTranslationX ? getTranslation() : 0; + if (header != null && header.isInTouchRect(x - translation, y)) { return true; } if ((!mIsSummaryWithChildren || shouldShowPublic()) @@ -3037,24 +3065,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override - public boolean topAmountNeedsClipping() { - if (isGroupExpanded()) { - return true; - } - if (isGroupExpansionChanging()) { - return true; - } - if (getShowingLayout().shouldClipToRounding(true /* topRounded */, - false /* bottomRounded */)) { - return true; - } - if (mGuts != null && mGuts.getAlpha() != 0.0f) { - return true; - } - return false; - } - - @Override protected boolean childNeedsClipping(View child) { if (child instanceof NotificationContentView) { NotificationContentView contentView = (NotificationContentView) child; 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 5134c62dc182..d58fe3b3c4a3 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 @@ -71,21 +71,19 @@ public abstract class ExpandableOutlineView extends ExpandableView { private int mBackgroundTop; /** - * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when + * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself. */ - protected boolean mShouldTranslateContents; - private boolean mTopAmountRounded; - private float mDistanceToTopRoundness = -1; + protected boolean mDismissUsingRowTranslationX = true; private float[] mTmpCornerRadii = new float[8]; private final ViewOutlineProvider mProvider = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { if (!mCustomOutline && getCurrentTopRoundness() == 0.0f - && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners - && !mTopAmountRounded) { - int translation = mShouldTranslateContents ? (int) getTranslation() : 0; + && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners) { + // Only when translating just the contents, does the outline need to be shifted. + int translation = !mDismissUsingRowTranslationX ? (int) getTranslation() : 0; int left = Math.max(translation, 0); int top = mClipTopAmount + mBackgroundTop; int right = getWidth() + Math.min(translation, 0); @@ -110,7 +108,9 @@ public abstract class ExpandableOutlineView extends ExpandableView { float topRoundness = mAlwaysRoundBothCorners ? mOutlineRadius : getCurrentBackgroundRadiusTop(); if (!mCustomOutline) { - int translation = mShouldTranslateContents && !ignoreTranslation + // The outline just needs to be shifted if we're translating the contents. Otherwise + // it's already in the right place. + int translation = !mDismissUsingRowTranslationX && !ignoreTranslation ? (int) getTranslation() : 0; int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f); left = Math.max(translation, 0) - halfExtraWidth; @@ -168,33 +168,15 @@ public abstract class ExpandableOutlineView extends ExpandableView { @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { canvas.save(); - Path intersectPath = null; - if (mTopAmountRounded && topAmountNeedsClipping()) { - int left = (int) (- mExtraWidthForClipping / 2.0f); - int top = (int) (mClipTopAmount - mDistanceToTopRoundness); - int right = getWidth() + (int) (mExtraWidthForClipping + left); - int bottom = (int) Math.max(mMinimumHeightForClipping, - Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius)); - getRoundedRectPath(left, top, right, bottom, mOutlineRadius, 0.0f, mClipPath); - intersectPath = mClipPath; - } - boolean clipped = false; if (childNeedsClipping(child)) { Path clipPath = getCustomClipPath(child); if (clipPath == null) { clipPath = getClipPath(false /* ignoreTranslation */); } if (clipPath != null) { - if (intersectPath != null) { - clipPath.op(intersectPath, Path.Op.INTERSECT); - } canvas.clipPath(clipPath); - clipped = true; } } - if (!clipped && intersectPath != null) { - canvas.clipPath(intersectPath); - } boolean result = super.drawChild(canvas, child, drawingTime); canvas.restore(); return result; @@ -212,32 +194,19 @@ public abstract class ExpandableOutlineView extends ExpandableView { invalidate(); } - @Override - public void setDistanceToTopRoundness(float distanceToTopRoundness) { - super.setDistanceToTopRoundness(distanceToTopRoundness); - if (distanceToTopRoundness != mDistanceToTopRoundness) { - mTopAmountRounded = distanceToTopRoundness >= 0; - mDistanceToTopRoundness = distanceToTopRoundness; - applyRoundness(); - } - } - protected boolean childNeedsClipping(View child) { return false; } - public boolean topAmountNeedsClipping() { - return true; - } - protected boolean isClippingNeeded() { - return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ; + // When translating the contents instead of the overall view, we need to make sure we clip + // rounded to the contents. + boolean forTranslation = getTranslation() != 0 && !mDismissUsingRowTranslationX; + return mAlwaysRoundBothCorners || mCustomOutline || forTranslation; } private void initDimens() { Resources res = getResources(); - mShouldTranslateContents = - res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe); mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius); mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline); if (!mAlwaysRoundBothCorners) { @@ -272,11 +241,6 @@ public abstract class ExpandableOutlineView extends ExpandableView { } public float getCurrentBackgroundRadiusTop() { - // If this view is top amount notification view, it should always has round corners on top. - // It will be applied with applyRoundness() - if (mTopAmountRounded) { - return mOutlineRadius; - } return getCurrentTopRoundness() * mOutlineRadius; } @@ -382,9 +346,25 @@ public abstract class ExpandableOutlineView extends ExpandableView { } } + /** + * Set the dismiss behavior of the view. + * @param usingRowTranslationX {@code true} if the view should translate using regular + * translationX, otherwise the contents will be + * translated. + */ + public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) { + mDismissUsingRowTranslationX = usingRowTranslationX; + } + @Override public int getOutlineTranslation() { - return mCustomOutline ? mOutlineRect.left : (int) getTranslation(); + if (mCustomOutline) { + return mOutlineRect.left; + } + if (mDismissUsingRowTranslationX) { + return 0; + } + return (int) getTranslation(); } public void updateOutline() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index 763d197847c3..8b0764b1c313 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -46,7 +46,6 @@ import java.util.List; public abstract class ExpandableView extends FrameLayout implements Dumpable { private static final String TAG = "ExpandableView"; - public static final float NO_ROUNDNESS = -1; protected OnHeightChangedListener mOnHeightChangedListener; private int mActualHeight; protected int mClipTopAmount; @@ -192,14 +191,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { } } - /** - * Set the distance to the top roundness, from where we should start clipping a value above - * or equal to 0 is the effective distance, and if a value below 0 is received, there should - * be no clipping. - */ - public void setDistanceToTopRoundness(float distanceToTopRoundness) { - } - public void setActualHeight(int actualHeight) { setActualHeight(actualHeight, true /* notifyListeners */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java index 4b1f679b8851..754de580cd61 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java @@ -42,10 +42,8 @@ public class NotificationBackgroundView extends View { private int mActualHeight; private int mClipBottomAmount; private int mTintColor; - private float[] mCornerRadii = new float[8]; + private final float[] mCornerRadii = new float[8]; private boolean mBottomIsRounded; - private boolean mLastInSection; - private boolean mFirstInSection; private int mBackgroundTop; private boolean mBottomAmountClips = true; private boolean mExpandAnimationRunning; @@ -53,9 +51,6 @@ public class NotificationBackgroundView extends View { private int mDrawableAlpha = 255; private boolean mIsPressedAllowed; - private boolean mTopAmountRounded; - private float mDistanceToTopRoundness; - public NotificationBackgroundView(Context context, AttributeSet attrs) { super(context, attrs); mDontModifyCorners = getResources().getBoolean( @@ -90,15 +85,6 @@ public class NotificationBackgroundView extends View { left = (int) ((getWidth() - mActualWidth) / 2.0f); right = (int) (left + mActualWidth); } - if (mTopAmountRounded) { - int clipTop = (int) (mClipTopAmount - mDistanceToTopRoundness); - if (clipTop >= 0 || !mFirstInSection) { - top += clipTop; - } - if (clipTop >= 0 && !mLastInSection) { - bottom += clipTop; - } - } drawable.setBounds(left, top, right, bottom); drawable.draw(canvas); } @@ -180,14 +166,6 @@ public class NotificationBackgroundView extends View { invalidate(); } - public void setDistanceToTopRoundness(float distanceToTopRoundness) { - if (distanceToTopRoundness != mDistanceToTopRoundness) { - mTopAmountRounded = distanceToTopRoundness >= 0; - mDistanceToTopRoundness = distanceToTopRoundness; - invalidate(); - } - } - @Override public boolean hasOverlappingRendering() { @@ -246,18 +224,6 @@ public class NotificationBackgroundView extends View { } } - /** Sets whether this background belongs to the last notification in a section. */ - public void setLastInSection(boolean lastInSection) { - mLastInSection = lastInSection; - invalidate(); - } - - /** Sets whether this background belongs to the first notification in a section. */ - public void setFirstInSection(boolean firstInSection) { - mFirstInSection = firstInSection; - invalidate(); - } - private void updateBackgroundRadii() { if (mDontModifyCorners) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 0c86262d9037..26606cda5582 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -75,7 +75,6 @@ public class AmbientState { private int mExpandAnimationTopChange; private ExpandableNotificationRow mExpandingNotification; private float mHideAmount; - private float mNotificationScrimTop; private boolean mAppearing; private float mPulseHeight = MAX_PULSE_HEIGHT; private float mDozeAmount = 0.0f; @@ -225,8 +224,14 @@ public class AmbientState { return mScrollY; } + /** + * Set the new Scroll Y position. + */ public void setScrollY(int scrollY) { - this.mScrollY = scrollY; + // Because we're dealing with an overscroller, scrollY could sometimes become smaller than + // 0. However this is only for internal purposes and the scroll position when read + // should never be smaller than 0, otherwise it can lead to flickers. + this.mScrollY = Math.max(scrollY, 0); } /** @@ -256,20 +261,6 @@ public class AmbientState { return mHideAmount; } - /** - * Set y position of top of notifications background scrim, relative to top of screen. - */ - public void setNotificationScrimTop(float notificationScrimTop) { - mNotificationScrimTop = notificationScrimTop; - } - - /** - * @return Y position of top of notifications background scrim, relative to top of screen. - */ - public float getNotificationScrimTop() { - return mNotificationScrimTop; - } - public void setHideSensitive(boolean hideSensitive) { mHideSensitive = hideSensitive; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 99fe541d0135..c2716b949b71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -710,10 +710,10 @@ public class NotificationChildrenContainer extends ViewGroup { if (mUserLocked) { expandFraction = getGroupExpandFraction(); } - final boolean dividersVisible = mUserLocked && !showingAsLowPriority() - || (mChildrenExpanded && mShowDividersWhenExpanded) - || (mContainingNotification.isGroupExpansionChanging() - && !mHideDividersDuringExpand); + final boolean isExpanding = !showingAsLowPriority() + && (mUserLocked || mContainingNotification.isGroupExpansionChanging()); + final boolean dividersVisible = (mChildrenExpanded && mShowDividersWhenExpanded) + || (isExpanding && !mHideDividersDuringExpand); for (int i = 0; i < childCount; i++) { ExpandableNotificationRow child = mAttachedChildren.get(i); ExpandableViewState viewState = child.getViewState(); @@ -725,7 +725,7 @@ public class NotificationChildrenContainer extends ViewGroup { tmpState.yTranslation = viewState.yTranslation - mDividerHeight; float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0; if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) { - alpha = NotificationUtils.interpolate(0, 0.5f, + alpha = NotificationUtils.interpolate(0, mDividerAlpha, Math.min(viewState.alpha, expandFraction)); } tmpState.hidden = !dividersVisible; @@ -789,10 +789,10 @@ public class NotificationChildrenContainer extends ViewGroup { int childCount = mAttachedChildren.size(); ViewState tmpState = new ViewState(); float expandFraction = getGroupExpandFraction(); - final boolean dividersVisible = mUserLocked && !showingAsLowPriority() - || (mChildrenExpanded && mShowDividersWhenExpanded) - || (mContainingNotification.isGroupExpansionChanging() - && !mHideDividersDuringExpand); + final boolean isExpanding = !showingAsLowPriority() + && (mUserLocked || mContainingNotification.isGroupExpansionChanging()); + final boolean dividersVisible = (mChildrenExpanded && mShowDividersWhenExpanded) + || (isExpanding && !mHideDividersDuringExpand); for (int i = childCount - 1; i >= 0; i--) { ExpandableNotificationRow child = mAttachedChildren.get(i); ExpandableViewState viewState = child.getViewState(); @@ -802,9 +802,9 @@ public class NotificationChildrenContainer extends ViewGroup { View divider = mDividers.get(i); tmpState.initFrom(divider); tmpState.yTranslation = viewState.yTranslation - mDividerHeight; - float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0; + float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0; if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) { - alpha = NotificationUtils.interpolate(0, 0.5f, + alpha = NotificationUtils.interpolate(0, mDividerAlpha, Math.min(viewState.alpha, expandFraction)); } tmpState.hidden = !dividersVisible; 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 f90b4c079c50..9ba04bf2f49f 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 @@ -40,6 +40,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Outline; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; @@ -110,6 +111,7 @@ import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.ScrollAdapter; import com.android.systemui.util.Assert; +import com.android.systemui.util.leak.RotationUtils; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -420,17 +422,26 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable animateScroll(); }; private int mCornerRadius; + private int mMinimumPaddings; + private int mQsTilePadding; + private boolean mSkinnyNotifsInLandscape; private int mSidePaddings; private final Rect mBackgroundAnimationRect = new Rect(); private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); private int mHeadsUpInset; + + /** + * The position of the scroll boundary relative to this view. This is where the notifications + * stop scrolling and will start to clip instead. + */ + private int mQsScrollBoundaryPosition; private HeadsUpAppearanceController mHeadsUpAppearanceController; private final Rect mTmpRect = new Rect(); private DismissListener mDismissListener; private DismissAllAnimationListener mDismissAllAnimationListener; private NotificationRemoteInputManager mRemoteInputManager; private ShadeController mShadeController; - private Runnable mOnStackYChanged; + private Consumer<Boolean> mOnStackYChanged; protected boolean mClearAllEnabled; @@ -453,6 +464,38 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationStackScrollLayoutController mController; private boolean mKeyguardMediaControllorVisible; + + /** + * The clip path used to clip the view in a rounded way. + */ + private final Path mRoundedClipPath = new Path(); + + /** + * Should we use rounded rect clipping right now + */ + private boolean mShouldUseRoundedRectClipping = false; + + private int mRoundedRectClippingLeft; + private int mRoundedRectClippingTop; + private int mRoundedRectClippingBottom; + private int mRoundedRectClippingRight; + private float[] mBgCornerRadii = new float[8]; + + /** + * Whether stackY should be animated in case the view is getting shorter than the scroll + * position and this scrolling will lead to the top scroll inset getting smaller. + */ + private boolean mAnimateStackYForContentHeightChange = false; + + /** + * Are we launching a notification right now + */ + private boolean mLaunchingNotification; + + /** + * Do notifications dismiss with normal transitioning + */ + private boolean mDismissUsingRowTranslationX = true; private NotificationEntry mTopHeadsUpEntry; private long mNumHeadsUp; private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; @@ -506,7 +549,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mSectionsManager = notificationSectionsManager; mFeatureFlags = featureFlags; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; - mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res); + updateSplitNotificationShade(); mSectionsManager.initialize(this, LayoutInflater.from(context)); mSections = mSectionsManager.createSectionsForBuckets(); @@ -609,6 +652,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mController.hasActiveClearableNotifications(ROWS_ALL); RemoteInputController remoteInputController = mRemoteInputManager.getController(); boolean showFooterView = (showDismissView || mController.hasActiveNotifications()) + && mEmptyShadeView.getVisibility() == GONE && mStatusBarState != StatusBarState.KEYGUARD && !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying() && (remoteInputController == null || !remoteInputController.isRemoteInputActive()); @@ -856,12 +900,32 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable R.dimen.min_top_overscroll_to_qs); mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height); mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom); - mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings); + mMinimumPaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings); + mQsTilePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal); + mSkinnyNotifsInLandscape = res.getBoolean(R.bool.config_skinnyNotifsInLandscape); + mSidePaddings = mMinimumPaddings; // Updated in onMeasure by updateSidePadding() mMinInteractionHeight = res.getDimensionPixelSize( R.dimen.notification_min_interaction_height); mCornerRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius); mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize( R.dimen.heads_up_status_bar_padding); + mQsScrollBoundaryPosition = res.getDimensionPixelSize( + com.android.internal.R.dimen.quick_qs_offset_height); + } + + void updateSidePadding(int viewWidth) { + if (viewWidth == 0 || !mSkinnyNotifsInLandscape) { + mSidePaddings = mMinimumPaddings; + return; + } + // Portrait is easy, just use the dimen for paddings + if (RotationUtils.getRotation(mContext) == RotationUtils.ROTATION_NONE) { + mSidePaddings = mMinimumPaddings; + return; + } + final int innerWidth = viewWidth - mMinimumPaddings * 2; + final int qsTileWidth = (innerWidth - mQsTilePadding * 3) / 4; + mSidePaddings = mMinimumPaddings + qsTileWidth + mQsTilePadding; } void updateCornerRadius() { @@ -924,6 +988,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); + updateSidePadding(width); int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2, MeasureSpec.getMode(widthMeasureSpec)); // Don't constrain the height of the children so we know how big they'd like to be @@ -961,6 +1026,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateFirstAndLastBackgroundViews(); updateAlgorithmLayoutMinHeight(); updateOwnTranslationZ(); + + // Once the layout has finished, we don't need to animate any scrolling clampings anymore. + mAnimateStackYForContentHeightChange = false; } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -1017,33 +1085,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private void onPreDrawDuringAnimation() { mShelf.updateAppearance(); - updateClippingToTopRoundedCorner(); if (!mNeedsAnimation && !mChildrenUpdateRequested) { updateBackground(); } } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void updateClippingToTopRoundedCorner() { - Float clipStart = mAmbientState.getNotificationScrimTop(); - Float clipEnd = clipStart + mCornerRadius; - boolean first = true; - for (int i = 0; i < getChildCount(); i++) { - ExpandableView child = (ExpandableView) getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - float start = child.getTranslationY(); - float end = start + child.getActualHeight(); - boolean clip = clipStart > start && clipStart < end - || clipEnd >= start && clipEnd <= end; - clip &= !(first && mScrollAdapter.isScrolledToTop()); - child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) - : ExpandableView.NO_ROUNDNESS); - first = false; - } - } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateScrollStateForAddedChildren() { if (mChildrenToAddAnimated.isEmpty()) { @@ -1117,7 +1163,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void clampScrollPosition() { int scrollRange = getScrollRange(); if (scrollRange < mOwnScrollY) { - setOwnScrollY(scrollRange); + boolean animateStackY = false; + if (scrollRange < getScrollAmountToScrollBoundary() + && mAnimateStackYForContentHeightChange) { + // if the scroll boundary updates the position of the stack, + animateStackY = true; + } + setOwnScrollY(scrollRange, animateStackY); } } @@ -1146,14 +1198,23 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * Apply expansion fraction to the y position and height of the notifications panel. */ private void updateStackPosition() { + updateStackPosition(false /* listenerNeedsAnimation */); + } + + /** + * Apply expansion fraction to the y position and height of the notifications panel. + * @param listenerNeedsAnimation does the listener need to animate? + */ + private void updateStackPosition(boolean listenerNeedsAnimation) { // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition - + mAmbientState.getOverExpansion(); + + mAmbientState.getOverExpansion() + - getCurrentOverScrollAmount(false /* top */); final float fraction = mAmbientState.getExpansionFraction(); final float stackY = MathUtils.lerp(0, endTopPosition, fraction); mAmbientState.setStackY(stackY); if (mOnStackYChanged != null) { - mOnStackYChanged.run(); + mOnStackYChanged.accept(listenerNeedsAnimation); } if (mQsExpansionFraction <= 0) { final float stackEndHeight = Math.max(0f, @@ -1165,7 +1226,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } - void setOnStackYChanged(Runnable onStackYChanged) { + /** + * Add a listener when the StackY changes. The argument signifies whether an animation is + * needed. + */ + void setOnStackYChanged(Consumer<Boolean> onStackYChanged) { mOnStackYChanged = onStackYChanged; } @@ -1600,7 +1665,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Resources res = getResources(); - mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res); + updateSplitNotificationShade(); mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height); float densityScale = res.getDisplayMetrics().density; mSwipeHelper.setDensityScale(densityScale); @@ -1866,6 +1931,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (onTop) { notifyOverscrollTopListener(amount, isRubberbanded); } + updateStackPosition(); requestChildrenUpdate(); } } @@ -2074,7 +2140,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateContentHeight() { - final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mSidePaddings; + final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings; int height = (int) scrimTopPadding; float previousPaddingRequest = mPaddingBetweenElements; int numShownItems = 0; @@ -2527,8 +2593,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateScrollStateForRemovedChild(child); boolean animationGenerated = generateRemoveAnimation(child); if (animationGenerated) { - if (!mSwipedOutViews.contains(child) - || Math.abs(child.getTranslation()) != child.getWidth()) { + if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) { container.addTransientView(child, 0); child.setTransientContainer(container); } @@ -2540,6 +2605,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable focusNextViewIfFocused(child); } + /** + * Has this view been fully swiped out such that it's not visible anymore. + */ + public boolean isFullySwipedOut(ExpandableView child) { + return Math.abs(child.getTranslation()) >= Math.abs(getTotalTranslationLength(child)); + } + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void focusNextViewIfFocused(View view) { if (view instanceof ExpandableNotificationRow) { @@ -2659,17 +2731,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable final int startingPosition = getPositionInLinearLayout(removedChild); final int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements; final int endPosition = startingPosition + childHeight; - if (endPosition <= mOwnScrollY) { + final int scrollBoundaryStart = getScrollAmountToScrollBoundary(); + mAnimateStackYForContentHeightChange = true; + // This is reset onLayout + if (endPosition <= mOwnScrollY - scrollBoundaryStart) { // This child is fully scrolled of the top, so we have to deduct its height from the // scrollPosition setOwnScrollY(mOwnScrollY - childHeight); - } else if (startingPosition < mOwnScrollY) { + } else if (startingPosition < mOwnScrollY - scrollBoundaryStart) { // This child is currently being scrolled into, set the scroll position to the // start of this child - setOwnScrollY(startingPosition); + setOwnScrollY(startingPosition + scrollBoundaryStart); } } + /** + * @return the amount of scrolling needed to start clipping notifications. + */ + private int getScrollAmountToScrollBoundary() { + return mTopPadding - mQsScrollBoundaryPosition; + } + @ShadeViewRefactor(RefactorComponent.COORDINATOR) private int getIntrinsicHeight(View view) { if (view instanceof ExpandableView) { @@ -2758,7 +2840,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateAnimationState(child); updateChronometerForChild(child); if (child instanceof ExpandableNotificationRow) { - ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl); + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + row.setDismissRtl(mDismissRtl); + row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX); + } } @@ -2818,6 +2903,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void applyExpandAnimationParams(ExpandAnimationParameters params) { mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange()); + + // Disable clipping for launches + setLaunchingNotification(params != null); requestChildrenUpdate(); } @@ -2901,7 +2989,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mAnimationEvents.clear(); updateBackground(); updateViewShadows(); - updateClippingToTopRoundedCorner(); } else { applyCurrentState(); } @@ -3030,7 +3117,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable removedTranslation = row.getTranslationWhenRemoved(); ignoreChildren = false; } - childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth(); + childWasSwipedOut |= isFullySwipedOut(row); } else if (child instanceof MediaHeaderView) { childWasSwipedOut = true; } @@ -3038,11 +3125,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable Rect clipBounds = child.getClipBounds(); childWasSwipedOut = clipBounds != null && clipBounds.height() == 0; - if (childWasSwipedOut && child instanceof ExpandableView) { + if (childWasSwipedOut) { // Clean up any potential transient views if the child has already been swiped // out, as we won't be animating it further (due to its height already being // clipped to 0. - ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer(); + ViewGroup transientContainer = child.getTransientContainer(); if (transientContainer != null) { transientContainer.removeTransientView(child); } @@ -3795,6 +3882,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateNotificationAnimationStates(); updateChronometers(); requestChildrenUpdate(); + updateUseRoundedRectClipping(); } } @@ -3815,6 +3903,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } void onChildHeightChanged(ExpandableView view, boolean needsAnimation) { + boolean previouslyNeededAnimation = mAnimateStackYForContentHeightChange; + if (needsAnimation) { + mAnimateStackYForContentHeightChange = true; + } updateContentHeight(); updateScrollPositionOnExpandInBottom(view); clampScrollPosition(); @@ -3835,6 +3927,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable requestAnimationOnViewResize(row); } requestChildrenUpdate(); + mAnimateStackYForContentHeightChange = previouslyNeededAnimation; } void onChildHeightReset(ExpandableView view) { @@ -4015,7 +4108,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable setAnimationRunning(false); updateBackground(); updateViewShadows(); - updateClippingToTopRoundedCorner(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4048,7 +4140,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable expandableView.setFakeShadowIntensity( diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD, previous.getOutlineAlpha(), (int) yLocation, - previous.getOutlineTranslation()); + (int) (previous.getOutlineTranslation() + previous.getTranslation())); } previous = expandableView; } @@ -4550,6 +4642,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setQsExpansionFraction(float qsExpansionFraction) { mQsExpansionFraction = qsExpansionFraction; + updateUseRoundedRectClipping(); // If notifications are scrolled, // clear out scrollY by the time we push notifications offscreen @@ -4560,13 +4653,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.COORDINATOR) private void setOwnScrollY(int ownScrollY) { + setOwnScrollY(ownScrollY, false /* animateScrollChangeListener */); + } + + @ShadeViewRefactor(RefactorComponent.COORDINATOR) + private void setOwnScrollY(int ownScrollY, boolean animateStackYChangeListener) { if (ownScrollY != mOwnScrollY) { // We still want to call the normal scrolled changed for accessibility reasons onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY); mOwnScrollY = ownScrollY; mAmbientState.setScrollY(mOwnScrollY); updateOnScrollChange(); - updateStackPosition(); + updateStackPosition(animateStackYChangeListener); } } @@ -4632,6 +4730,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mStatusBarState = statusBarState; mAmbientState.setStatusBarState(statusBarState); updateSpeedBumpIndex(); + updateDismissBehavior(); } void onStatePostChange(boolean fromShadeLocked) { @@ -5192,6 +5291,108 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } /** + * Set rounded rect clipping bounds on this view. + */ + public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius, + int bottomRadius) { + if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right + && mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top + && mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) { + return; + } + mRoundedRectClippingLeft = left; + mRoundedRectClippingTop = top; + mRoundedRectClippingBottom = bottom; + mRoundedRectClippingRight = right; + mBgCornerRadii[0] = topRadius; + mBgCornerRadii[1] = topRadius; + mBgCornerRadii[2] = topRadius; + mBgCornerRadii[3] = topRadius; + mBgCornerRadii[4] = bottomRadius; + mBgCornerRadii[5] = bottomRadius; + mBgCornerRadii[6] = bottomRadius; + mBgCornerRadii[7] = bottomRadius; + mRoundedClipPath.reset(); + mRoundedClipPath.addRoundRect(left, top, right, bottom, mBgCornerRadii, Path.Direction.CW); + if (mShouldUseRoundedRectClipping) { + invalidate(); + } + } + + private void updateSplitNotificationShade() { + boolean split = shouldUseSplitNotificationShade(mFeatureFlags, getResources()); + if (split != mShouldUseSplitNotificationShade) { + mShouldUseSplitNotificationShade = split; + updateDismissBehavior(); + updateUseRoundedRectClipping(); + } + } + + private void updateDismissBehavior() { + // On the split keyguard, dismissing with clipping without a visual boundary looks odd, + // so let's use the content dismiss behavior instead. + boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade + || mStatusBarState != StatusBarState.KEYGUARD; + if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) { + mDismissUsingRowTranslationX = dismissUsingRowTranslationX; + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof ExpandableNotificationRow) { + ((ExpandableNotificationRow) child).setDismissUsingRowTranslationX( + dismissUsingRowTranslationX); + } + } + } + } + + /** + * Set if we're launching a notification right now. + */ + private void setLaunchingNotification(boolean launching) { + if (launching == mLaunchingNotification) { + return; + } + mLaunchingNotification = launching; + updateUseRoundedRectClipping(); + } + + /** + * Should we use rounded rect clipping + */ + private void updateUseRoundedRectClipping() { + // We don't want to clip notifications when QS is expanded, because incoming heads up on + // the bottom would be clipped otherwise + boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade; + boolean clip = !mLaunchingNotification && mIsExpanded && qsAllowsClipping; + if (clip != mShouldUseRoundedRectClipping) { + mShouldUseRoundedRectClipping = clip; + invalidate(); + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (mShouldUseRoundedRectClipping) { + // Let's clip rounded. + canvas.clipPath(mRoundedClipPath); + } + super.dispatchDraw(canvas); + } + + /** + * Calculate the total translation needed when dismissing. + */ + public float getTotalTranslationLength(View animView) { + if (!mDismissUsingRowTranslationX) { + return animView.getMeasuredWidth(); + } + float notificationWidth = animView.getMeasuredWidth(); + int containerWidth = getMeasuredWidth(); + float padding = (containerWidth - notificationWidth) / 2.0f; + return containerWidth - padding; + } + + /** * A listener that is notified when the empty space below the notifications is clicked on */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index dec98887577e..495eda772219 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -384,6 +384,11 @@ public class NotificationStackScrollLayoutController { } @Override + public float getTotalTranslationLength(View animView) { + return mView.getTotalTranslationLength(animView); + } + + @Override public void onSnooze(StatusBarNotification sbn, NotificationSwipeActionHelper.SnoozeOption snoozeOption) { mStatusBar.setNotificationSnoozed(sbn, snoozeOption); @@ -822,8 +827,18 @@ public class NotificationStackScrollLayoutController { return mView.isLayoutRtl(); } + /** + * @return the left of the view. + */ public int getLeft() { - return mView.getLeft(); + return mView.getLeft(); + } + + /** + * @return the top of the view. + */ + public int getTop() { + return mView.getTop(); } public float getTranslationX() { @@ -1008,7 +1023,7 @@ public class NotificationStackScrollLayoutController { mView.setQsExpansionFraction(expansionFraction); } - public void setOnStackYChanged(Runnable onStackYChanged) { + public void setOnStackYChanged(Consumer<Boolean> onStackYChanged) { mView.setOnStackYChanged(onStackYChanged); } @@ -1056,10 +1071,6 @@ public class NotificationStackScrollLayoutController { mView.setAlpha(alpha); } - public float getCurrentOverScrollAmount(boolean top) { - return mView.getCurrentOverScrollAmount(top); - } - public float calculateAppearFraction(float height) { return mView.calculateAppearFraction(height); } @@ -1440,6 +1451,14 @@ public class NotificationStackScrollLayoutController { } /** + * Set rounded rect clipping bounds on this view. + */ + public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius, + int bottomRadius) { + mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius); + } + + /** * Enum for UiEvent logged from this class */ enum NotificationPanelEvent implements UiEventLogger.UiEventEnum { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index f4c4d440b063..664776975b24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -325,6 +325,11 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc } @Override + protected float getTotalTranslationLength(View animView) { + return mCallback.getTotalTranslationLength(animView); + } + + @Override public void setTranslation(View v, float translate) { if (v instanceof SwipeableView) { ((SwipeableView) v).setTranslation(translate); @@ -466,6 +471,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption); void onDismiss(); + + /** + * Get the total translation length where we want to swipe to when dismissing the view. By + * default this is the size of the view, but can also be larger. + * @param animView the view to ask about + */ + float getTotalTranslationLength(View animView); } static class Builder { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index e5fd103c239f..8f4a71cf2563 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -158,7 +158,7 @@ public class StackScrollAlgorithm { AmbientState ambientState) { float drawStart = ambientState.isOnKeyguard() ? 0 : ambientState.getStackY() - ambientState.getScrollY(); - float clipStart = ambientState.getNotificationScrimTop(); + float clipStart = 0; int childCount = algorithmState.visibleChildren.size(); boolean firstHeadsUp = true; for (int i = 0; i < childCount; i++) { @@ -218,13 +218,7 @@ public class StackScrollAlgorithm { */ private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state, AmbientState ambientState) { - float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */); - int scrollY = ambientState.getScrollY(); - - // Due to the overScroller, the stackscroller can have negative scroll state. This is - // already accounted for by the top padding and doesn't need an additional adaption - scrollY = Math.max(0, scrollY); - state.scrollY = (int) (scrollY + bottomOverScroll); + state.scrollY = ambientState.getScrollY(); state.mCurrentYPosition = -state.scrollY; state.mCurrentExpandedYPosition = -state.scrollY; @@ -261,7 +255,7 @@ public class StackScrollAlgorithm { // Save the index of first view in shelf from when shade is fully // expanded. Consider updating these states in updateContentView instead so that we don't // have to recalculate in every frame. - float currentY = -scrollY; + float currentY = -ambientState.getScrollY(); if (!ambientState.isOnKeyguard()) { currentY += mNotificationScrimPadding; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 4fd2064b394d..ee12b4b2d728 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -392,7 +392,7 @@ public class StackStateAnimator { 0, () -> removeTransientView(changingView), null); } else if (event.animationType == NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) { - if (Math.abs(changingView.getTranslation()) == changingView.getWidth() + if (mHostLayout.isFullySwipedOut(changingView) && changingView.getTransientContainer() != null) { changingView.getTransientContainer().removeTransientView(changingView); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 01d489f91de2..c4d1abc1b74c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -63,6 +63,7 @@ public class DozeParameters implements TunerService.Tunable, private final Resources mResources; private final BatteryController mBatteryController; private final FeatureFlags mFeatureFlags; + private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private final Set<Callback> mCallbacks = new HashSet<>(); @@ -78,7 +79,8 @@ public class DozeParameters implements TunerService.Tunable, BatteryController batteryController, TunerService tunerService, DumpManager dumpManager, - FeatureFlags featureFlags) { + FeatureFlags featureFlags, + UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) { mResources = resources; mAmbientDisplayConfiguration = ambientDisplayConfiguration; mAlwaysOnPolicy = alwaysOnDisplayPolicy; @@ -89,6 +91,7 @@ public class DozeParameters implements TunerService.Tunable, mPowerManager = powerManager; mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation); mFeatureFlags = featureFlags; + mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; tunerService.addTunable( this, @@ -220,7 +223,8 @@ public class DozeParameters implements TunerService.Tunable, * then abruptly showing AOD. */ public boolean shouldControlUnlockedScreenOff() { - return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations(); + return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations() + && mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation(); } private boolean getBoolean(String propName, int resId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index 68e20705fbeb..96276f46d23d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -38,7 +38,7 @@ import java.util.LinkedList; * A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open"). */ public class KeyguardIndicationTextView extends TextView { - private static final long MSG_DURATION_MILLIS = 600; + private static final long MSG_DURATION_MILLIS = 1500; private long mNextAnimationTime = 0; private boolean mAnimationsEnabled = true; private LinkedList<CharSequence> mMessages = new LinkedList<>(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 528827f639dd..aaddfca4b685 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -830,6 +830,7 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationStackScrollLayoutController.setOverscrollTopChangedListener( mOnOverscrollTopChangedListener); mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled); + mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged); mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( mOnEmptySpaceClickListener); addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); @@ -2198,15 +2199,17 @@ public class NotificationPanelViewController extends PanelViewController { mDepthController.setQsPanelExpansion(qsExpansionFraction); } - private Runnable mOnStackYChanged = () -> { + private void onStackYChanged(boolean shouldAnimate) { if (mQs != null) { + if (shouldAnimate) { + mAnimateNextNotificationBounds = true; + mNotificationBoundsAnimationDelay = 0; + } setQSClippingBounds(); } }; private void onNotificationScrolled(int newScrollPosition) { - // Since this is an overscroller, sometimes the scrollY can be temporarily negative - // (when overscrollng on the top and flinging). Let's updateQSExpansionEnabledAmbient(); } @@ -2228,14 +2231,13 @@ public class NotificationPanelViewController extends PanelViewController { * and QS state. */ private void setQSClippingBounds() { - int top = 0; - int bottom = 0; - int left = 0; - int right = 0; + int top; + int bottom; + int left; + int right; final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction()); - final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0) - && !mShouldUseSplitNotificationShade; + final boolean qsVisible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0); if (!mShouldUseSplitNotificationShade) { if (mTransitioningToFullShadeProgress > 0.0f) { @@ -2244,7 +2246,6 @@ public class NotificationPanelViewController extends PanelViewController { top = mTransitionToFullShadeQSPosition; } else { final float notificationTop = getQSEdgePosition(); - mAmbientState.setNotificationScrimTop(notificationTop); top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop) : notificationTop); } @@ -2252,8 +2253,7 @@ public class NotificationPanelViewController extends PanelViewController { // notification bounds should take full screen width regardless of insets left = 0; right = getView().getRight() + mDisplayRightInset; - } else if (qsPanelBottomY > 0) { // so bounds are empty on lockscreen - mAmbientState.setNotificationScrimTop(mSplitShadeNotificationsTopPadding); + } else { top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding); bottom = mNotificationStackScrollLayoutController.getHeight(); left = mNotificationStackScrollLayoutController.getLeft(); @@ -2261,17 +2261,17 @@ public class NotificationPanelViewController extends PanelViewController { } // top should never be lower than bottom, otherwise it will be invisible. top = Math.min(top, bottom); - applyQSClippingBounds(left, top, right, bottom, visible); + applyQSClippingBounds(left, top, right, bottom, qsVisible); } private void applyQSClippingBounds(int left, int top, int right, int bottom, - boolean visible) { + boolean qsVisible) { if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) { if (mQsClippingAnimation != null) { // update the end position of the animator mQsClippingAnimationEndBounds.set(left, top, right, bottom); } else { - applyQSClippingImmediately(left, top, right, bottom, visible); + applyQSClippingImmediately(left, top, right, bottom, qsVisible); } } else { mQsClippingAnimationEndBounds.set(left, top, right, bottom); @@ -2295,7 +2295,7 @@ public class NotificationPanelViewController extends PanelViewController { int animBottom = (int) MathUtils.lerp(startBottom, mQsClippingAnimationEndBounds.bottom, fraction); applyQSClippingImmediately(animLeft, animTop, animRight, animBottom, - visible /* visible */); + qsVisible /* qsVisible */); }); mQsClippingAnimation.addListener(new AnimatorListenerAdapter() { @Override @@ -2310,7 +2310,7 @@ public class NotificationPanelViewController extends PanelViewController { } private void applyQSClippingImmediately(int left, int top, int right, int bottom, - boolean visible) { + boolean qsVisible) { // Fancy clipping for quick settings int radius = mScrimCornerRadius; int statusBarClipTop = 0; @@ -2318,19 +2318,34 @@ public class NotificationPanelViewController extends PanelViewController { if (!mShouldUseSplitNotificationShade) { // The padding on this area is large enough that we can use a cheaper clipping strategy mKeyguardStatusAreaClipBounds.set(left, top, right, bottom); - clipStatusView = visible; + clipStatusView = qsVisible; radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius, Math.min(top / (float) mScrimCornerRadius, 1f)); statusBarClipTop = top - mKeyguardStatusBar.getTop(); } if (mQs != null) { - mQs.setFancyClipping(top, bottom, radius, visible); + mQs.setFancyClipping(top, bottom, radius, qsVisible + && !mShouldUseSplitNotificationShade); } mKeyguardStatusViewController.setClipBounds( clipStatusView ? mKeyguardStatusAreaClipBounds : null); - mScrimController.setNotificationsBounds(left, top, right, bottom); + if (!qsVisible && mShouldUseSplitNotificationShade) { + // On the lockscreen when qs isn't visible, we don't want the bounds of the shade to + // be visible, otherwise you can see the bounds once swiping up to see bouncer + mScrimController.setNotificationsBounds(0, 0, 0, 0); + } else { + mScrimController.setNotificationsBounds(left, top, right, bottom); + } + mScrimController.setScrimCornerRadius(radius); mKeyguardStatusBar.setTopClipping(statusBarClipTop); + int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft(); + int nsslRight = right - mNotificationStackScrollLayoutController.getLeft(); + int nsslTop = top - mNotificationStackScrollLayoutController.getTop(); + int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop(); + int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0; + mNotificationStackScrollLayoutController.setRoundedClippingBounds( + nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius); } private float getQSEdgePosition() { @@ -3323,7 +3338,6 @@ public class NotificationPanelViewController extends PanelViewController { // The expandedHeight is always the full panel Height when bypassing expandedHeight = getMaxPanelHeightNonBypass(); } - mNotificationStackScrollLayoutController.setOnStackYChanged(mOnStackYChanged); mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight); updateKeyguardBottomAreaAlpha(); updateBigClockAlpha(); @@ -4222,7 +4236,7 @@ public class NotificationPanelViewController extends PanelViewController { int oldState = mBarState; boolean keyguardShowing = statusBarState == KEYGUARD; - if (mUnlockedScreenOffAnimationController.shouldPlayScreenOffAnimation() + if (mDozeParameters.shouldControlUnlockedScreenOff() && oldState == StatusBarState.SHADE && statusBarState == KEYGUARD) { // This means we're doing the screen off animation - position the keyguard status diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index 52f9aca82783..c95879650049 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -26,11 +26,9 @@ import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENAB import android.app.IActivityManager; import android.content.Context; import android.content.pm.ActivityInfo; -import android.content.res.Resources; import android.graphics.PixelFormat; import android.os.Binder; import android.os.RemoteException; -import android.os.SystemProperties; import android.os.Trace; import android.util.Log; import android.view.Display; @@ -53,6 +51,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.google.android.collect.Lists; @@ -108,12 +107,14 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW StatusBarStateController statusBarStateController, ConfigurationController configurationController, KeyguardViewMediator keyguardViewMediator, - KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor, - DumpManager dumpManager) { + KeyguardBypassController keyguardBypassController, + SysuiColorExtractor colorExtractor, + DumpManager dumpManager, + KeyguardStateController keyguardStateController) { mContext = context; mWindowManager = windowManager; mActivityManager = activityManager; - mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation(); + mKeyguardScreenRotation = keyguardStateController.isKeyguardScreenRotationAllowed(); mDozeParameters = dozeParameters; mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze(); mLpChanged = new LayoutParams(); @@ -173,12 +174,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW } } - private boolean shouldEnableKeyguardScreenRotation() { - Resources res = mContext.getResources(); - return SystemProperties.getBoolean("lockscreen.rot_override", false) - || res.getBoolean(R.bool.config_enableLockScreenRotation); - } - /** * Adds the notification shade view to the window manager. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 7a1e5cf1770b..f6c18fbb8f24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -1242,6 +1242,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump pw.println(mDefaultScrimAlpha); pw.print(" mExpansionFraction="); pw.println(mPanelExpansion); + + pw.print(" mState.getMaxLightRevealScrimAlpha="); + pw.println(mState.getMaxLightRevealScrimAlpha()); } public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index e52e1fa5f39f..06811932ac0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -197,7 +197,7 @@ public enum ScrimState { } @Override - public float getBehindAlpha() { + public float getMaxLightRevealScrimAlpha() { return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f; } @@ -220,18 +220,11 @@ public enum ScrimState { mBlankScreen = mDisplayRequiresBlanking; mAnimationDuration = mWakeLockScreenSensorActive ? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION; - - // Wake sensor will show the wallpaper, let's fade from black. Otherwise it will - // feel like the screen is flashing if the wallpaper is light. - if (mWakeLockScreenSensorActive && previousState == AOD) { - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); - } } - @Override - public float getBehindAlpha() { + public float getMaxLightRevealScrimAlpha() { return mWakeLockScreenSensorActive ? ScrimController.WAKE_SENSOR_SCRIM_ALPHA - : AOD.getBehindAlpha(); + : AOD.getMaxLightRevealScrimAlpha(); } }, @@ -351,6 +344,10 @@ public enum ScrimState { return mBehindAlpha; } + public float getMaxLightRevealScrimAlpha() { + return 1f; + } + public float getNotifAlpha() { return mNotifAlpha; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 5ee5e489479d..3412a3a507dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; -import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.containsType; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; @@ -46,8 +45,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING; import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; import static com.android.wm.shell.bubbles.BubbleController.TASKBAR_CHANGED_BROADCAST; -import android.animation.ValueAnimator; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -121,6 +118,7 @@ import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import android.widget.DateTimeView; +import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; @@ -402,8 +400,6 @@ public class StatusBar extends SystemUI implements DemoMode, private LightRevealScrim mLightRevealScrim; private WiredChargingRippleController mChargingRippleAnimationController; private PowerButtonReveal mPowerButtonReveal; - private CircleReveal mCircleReveal; - private ValueAnimator mCircleRevealAnimator = ValueAnimator.ofFloat(0f, 1f); private final Object mQueueLock = new Object(); @@ -2778,9 +2774,14 @@ public class StatusBar extends SystemUI implements DemoMode, + String.valueOf(CameraIntents.getOverrideCameraPackage(mContext))); } - public static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) { + public static void dumpBarTransitions( + PrintWriter pw, String var, @Nullable BarTransitions transitions) { pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode="); - pw.println(BarTransitions.modeToString(transitions.getMode())); + if (transitions != null) { + pw.println(BarTransitions.modeToString(transitions.getMode())); + } else { + pw.println("Unknown"); + } } public void createAndAddWindows(@Nullable RegisterStatusBarResult result) { @@ -2803,11 +2804,11 @@ public class StatusBar extends SystemUI implements DemoMode, return mDisplayMetrics.density; } - float getDisplayWidth() { + public float getDisplayWidth() { return mDisplayMetrics.widthPixels; } - float getDisplayHeight() { + public float getDisplayHeight() { return mDisplayMetrics.heightPixels; } @@ -2841,9 +2842,11 @@ public class StatusBar extends SystemUI implements DemoMode, mActivityIntentHelper.wouldLaunchResolverActivity(intent, mLockscreenUserManager.getCurrentUserId()); + boolean animate = + animationController != null && !willLaunchResolverActivity && shouldAnimateLaunch( + true /* isActivityIntent */); ActivityLaunchAnimator.Controller animController = - !willLaunchResolverActivity && shouldAnimateLaunch(true /* isActivityIntent */) - ? wrapAnimationController(animationController, dismissShade) : null; + animate ? wrapAnimationController(animationController, dismissShade) : null; // If we animate, we will dismiss the shade only once the animation is done. This is taken // care of by the StatusBarLaunchAnimationController. @@ -2857,7 +2860,7 @@ public class StatusBar extends SystemUI implements DemoMode, int[] result = new int[]{ActivityManager.START_CANCELED}; mActivityLaunchAnimator.startIntentWithAnimation(animController, - true /* animate */, intent.getPackage(), (adapter) -> { + animate, intent.getPackage(), (adapter) -> { ActivityOptions options = new ActivityOptions( getActivityOptions(mDisplayId, adapter)); options.setDisallowEnterPictureInPictureWhileLaunching( @@ -2907,16 +2910,12 @@ public class StatusBar extends SystemUI implements DemoMode, } }; executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly, - willLaunchResolverActivity, true /* deferred */); + willLaunchResolverActivity, true /* deferred */, animate); } @Nullable private ActivityLaunchAnimator.Controller wrapAnimationController( - @Nullable ActivityLaunchAnimator.Controller animationController, boolean dismissShade) { - if (animationController == null) { - return null; - } - + ActivityLaunchAnimator.Controller animationController, boolean dismissShade) { View rootView = animationController.getLaunchContainer().getRootView(); if (rootView == mSuperStatusBarViewFactory.getStatusBarWindowView()) { // We are animating a view in the status bar. We have to make sure that the status bar @@ -2959,34 +2958,56 @@ public class StatusBar extends SystemUI implements DemoMode, final boolean dismissShade, final boolean afterKeyguardGone, final boolean deferred) { - dismissKeyguardThenExecute(() -> { - if (runnable != null) { - if (mStatusBarKeyguardViewManager.isShowing() - && mStatusBarKeyguardViewManager.isOccluded()) { - mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); - } else { - AsyncTask.execute(runnable); - } - } - if (dismissShade) { - if (mExpandedVisible && !mBouncerShowing) { - mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, - true /* force */, true /* delayed*/); - } else { + executeRunnableDismissingKeyguard(runnable, cancelAction, dismissShade, afterKeyguardGone, + deferred, false /* willAnimateOnKeyguard */); + } - // Do it after DismissAction has been processed to conserve the needed ordering. - mHandler.post(mShadeController::runPostCollapseRunnables); + public void executeRunnableDismissingKeyguard(final Runnable runnable, + final Runnable cancelAction, + final boolean dismissShade, + final boolean afterKeyguardGone, + final boolean deferred, + final boolean willAnimateOnKeyguard) { + OnDismissAction onDismissAction = new OnDismissAction() { + @Override + public boolean onDismiss() { + if (runnable != null) { + if (mStatusBarKeyguardViewManager.isShowing() + && mStatusBarKeyguardViewManager.isOccluded()) { + mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); + } else { + AsyncTask.execute(runnable); + } + } + if (dismissShade) { + if (mExpandedVisible && !mBouncerShowing) { + mShadeController.animateCollapsePanels( + CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, + true /* force */, true /* delayed*/); + } else { + + // Do it after DismissAction has been processed to conserve the needed + // ordering. + mHandler.post(mShadeController::runPostCollapseRunnables); + } + } else if (StatusBar.this.isInLaunchTransition() + && mNotificationPanelViewController.isLaunchTransitionFinished()) { + + // We are not dismissing the shade, but the launch transition is already + // finished, + // so nobody will call readyForKeyguardDone anymore. Post it such that + // keyguardDonePending gets called first. + mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone); } - } else if (isInLaunchTransition() - && mNotificationPanelViewController.isLaunchTransitionFinished()) { + return deferred; + } - // We are not dismissing the shade, but the launch transition is already finished, - // so nobody will call readyForKeyguardDone anymore. Post it such that - // keyguardDonePending gets called first. - mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone); + @Override + public boolean willRunAnimationOnKeyguard() { + return willAnimateOnKeyguard; } - return deferred; - }, cancelAction, afterKeyguardGone); + }; + dismissKeyguardThenExecute(onDismissAction, cancelAction, afterKeyguardGone); } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -3408,6 +3429,10 @@ public class StatusBar extends SystemUI implements DemoMode, } boolean updateIsKeyguard() { + return updateIsKeyguard(false /* force */); + } + + boolean updateIsKeyguard(boolean force) { boolean wakeAndUnlocking = mBiometricUnlockController.getMode() == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; @@ -3431,7 +3456,7 @@ public class StatusBar extends SystemUI implements DemoMode, showKeyguardImpl(); } } else { - return hideKeyguardImpl(); + return hideKeyguardImpl(force); } return false; } @@ -3513,9 +3538,6 @@ public class StatusBar extends SystemUI implements DemoMode, public void fadeKeyguardWhilePulsing() { mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING, ()-> { - if (shouldShowCircleReveal()) { - startCircleReveal(); - } hideKeyguard(); mStatusBarKeyguardViewManager.onKeyguardFadedAway(); }).start(); @@ -3560,11 +3582,11 @@ public class StatusBar extends SystemUI implements DemoMode, /** * @return true if we would like to stay in the shade, false if it should go away entirely */ - public boolean hideKeyguardImpl() { + public boolean hideKeyguardImpl(boolean force) { mIsKeyguard = false; Trace.beginSection("StatusBar#hideKeyguard"); boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide(); - if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) { + if (!(mStatusBarStateController.setState(StatusBarState.SHADE, force))) { //TODO: StatusBarStateController should probably know about hiding the keyguard and // notify listeners. @@ -3856,7 +3878,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onDozeAmountChanged(float linear, float eased) { if (mFeatureFlags.useNewLockscreenAnimations() - && !mCircleRevealAnimator.isRunning()) { + && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { mLightRevealScrim.setRevealAmount(1f - linear); } } @@ -3879,7 +3901,7 @@ public class StatusBar extends SystemUI implements DemoMode, || (!isDozing && mWakefulnessLifecycle.getLastWakeReason() == PowerManager.WAKE_REASON_POWER_BUTTON)) { mLightRevealScrim.setRevealEffect(mPowerButtonReveal); - } else if (!mCircleRevealAnimator.isRunning()) { + } else if (!(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE); } @@ -3891,36 +3913,8 @@ public class StatusBar extends SystemUI implements DemoMode, Trace.endSection(); } - /** - * Update the parameters for the dozing circle reveal that animates when the user authenticates - * from AOD using the fingerprint sensor. - */ - public void updateCircleReveal() { - final PointF fpLocation = mAuthRippleController.getFingerprintSensorLocation(); - if (fpLocation != null) { - mCircleReveal = - new CircleReveal( - fpLocation.x, - fpLocation.y, - 0, - Math.max(Math.max(fpLocation.x, getDisplayWidth() - fpLocation.x), - Math.max(fpLocation.y, getDisplayHeight() - fpLocation.y))); - } - } - - private void startCircleReveal() { - mLightRevealScrim.setRevealEffect(mCircleReveal); - mCircleRevealAnimator.cancel(); - mCircleRevealAnimator.addUpdateListener(animation -> - mLightRevealScrim.setRevealAmount( - (float) mCircleRevealAnimator.getAnimatedValue())); - mCircleRevealAnimator.setDuration(900); - mCircleRevealAnimator.start(); - } - - private boolean shouldShowCircleReveal() { - return mCircleReveal != null && !mCircleRevealAnimator.isRunning() - && mBiometricUnlockController.getBiometricType() == FINGERPRINT; + public LightRevealScrim getLightRevealScrim() { + return mLightRevealScrim; } private void updateKeyguardState() { @@ -4060,7 +4054,7 @@ public class StatusBar extends SystemUI implements DemoMode, // The screen off animation uses our LightRevealScrim - we need to be expanded for it to // be visible. - if (mUnlockedScreenOffAnimationController.shouldPlayScreenOffAnimation()) { + if (mDozeParameters.shouldControlUnlockedScreenOff()) { makeExpandedVisible(true); } @@ -4450,6 +4444,8 @@ public class StatusBar extends SystemUI implements DemoMode, } else { mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } + updateLightRevealScrimVisibility(); + Trace.endSection(); } @@ -4609,28 +4605,37 @@ public class StatusBar extends SystemUI implements DemoMode, * * @param action The action to execute after dismissing the keyguard. * @param collapsePanel Whether we should collapse the panel after dismissing the keyguard. - * @param deferKeyguardDismiss Whether we should defer the keyguard actual dismissal, for - * instance to run animations on the keyguard before hiding it. + * @param willAnimateOnKeyguard Whether {@param action} will run an animation on the keyguard if + * we are locked. */ private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone, - boolean collapsePanel, boolean deferKeyguardDismiss) { + boolean collapsePanel, boolean willAnimateOnKeyguard) { if (!mDeviceProvisionedController.isDeviceProvisioned()) return; - dismissKeyguardThenExecute(() -> { - new Thread(() -> { - try { - // The intent we are sending is for the application, which - // won't have permission to immediately start an activity after - // the user switches to home. We know it is safe to do at this - // point, so make sure new activity switches are now allowed. - ActivityManager.getService().resumeAppSwitches(); - } catch (RemoteException e) { - } - action.run(); - }).start(); + OnDismissAction onDismissAction = new OnDismissAction() { + @Override + public boolean onDismiss() { + new Thread(() -> { + try { + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManager.getService().resumeAppSwitches(); + } catch (RemoteException e) { + } + action.run(); + }).start(); - return collapsePanel ? mShadeController.collapsePanel() : deferKeyguardDismiss; - }, afterKeyguardGone); + return collapsePanel ? mShadeController.collapsePanel() : willAnimateOnKeyguard; + } + + @Override + public boolean willRunAnimationOnKeyguard() { + return willAnimateOnKeyguard; + } + }; + dismissKeyguardThenExecute(onDismissAction, afterKeyguardGone); } @Override @@ -4674,7 +4679,6 @@ public class StatusBar extends SystemUI implements DemoMode, // the animation on the keyguard). The animation will take care of (instantly) collapsing // the shade and hiding the keyguard once it is done. boolean collapse = !animate; - boolean deferKeyguardDismiss = animate; executeActionDismissingKeyguard(() -> { try { // We wrap animationCallback with a StatusBarLaunchAnimatorController so that the @@ -4703,7 +4707,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (intentSentUiThreadCallback != null) { postOnUiThread(intentSentUiThreadCallback); } - }, willLaunchResolverActivity, collapse, deferKeyguardDismiss); + }, willLaunchResolverActivity, collapse, animate); } private void postOnUiThread(Runnable runnable) { @@ -4892,6 +4896,7 @@ public class StatusBar extends SystemUI implements DemoMode, return; } + mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha()); if (mFeatureFlags.useNewLockscreenAnimations() && (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) { mLightRevealScrim.setVisibility(View.VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index c7efcb2923e7..e8463992ed13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -192,6 +192,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private OnDismissAction mAfterKeyguardGoneAction; private Runnable mKeyguardGoneCancelAction; + private boolean mDismissActionWillAnimateOnKeyguard; private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>(); // Dismiss action to be launched when we stop dozing or the keyguard is gone. @@ -447,6 +448,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mAfterKeyguardGoneAction = r; mKeyguardGoneCancelAction = cancelAction; + mDismissActionWillAnimateOnKeyguard = r != null && r.willRunAnimationOnKeyguard(); // If there is an an alternate auth interceptor (like the UDFPS), show that one instead // of the bouncer. @@ -625,9 +627,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncer.startPreHideAnimation(finishRunnable); mStatusBar.onBouncerPreHideAnimation(); - // startPreHideAnimation() will change the visibility of the bouncer, so we have to - // make sure to update its state. - updateStates(); + // We update the state (which will show the keyguard) only if an animation will run on + // the keyguard. If there is no animation, we wait before updating the state so that we + // go directly from bouncer to launcher/app. + if (mDismissActionWillAnimateOnKeyguard) { + updateStates(); + } } else if (finishRunnable != null) { finishRunnable.run(); } @@ -798,6 +803,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mAfterKeyguardGoneAction = null; } mKeyguardGoneCancelAction = null; + mDismissActionWillAnimateOnKeyguard = false; for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) { mAfterKeyguardGoneRunnables.get(i).run(); } @@ -866,6 +872,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; // allow bouncer to trigger saved actions } mAfterKeyguardGoneAction = null; + mDismissActionWillAnimateOnKeyguard = false; if (mKeyguardGoneCancelAction != null) { mKeyguardGoneCancelAction.run(); mKeyguardGoneCancelAction = null; @@ -892,6 +899,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb }; protected void updateStates() { + if (mContainer == null ) { + return; + } int vis = mContainer.getSystemUiVisibility(); boolean showing = mShowing; boolean occluded = mOccluded; 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 d93b76646d58..98b9cc9bc716 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -41,6 +41,7 @@ import android.text.TextUtils; import android.util.EventLog; import android.view.View; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.widget.LockPatternUtils; @@ -260,10 +261,19 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null && mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); - ActivityStarter.OnDismissAction postKeyguardAction = - () -> handleNotificationClickAfterKeyguardDismissed( + ActivityStarter.OnDismissAction postKeyguardAction = new ActivityStarter.OnDismissAction() { + @Override + public boolean onDismiss() { + return handleNotificationClickAfterKeyguardDismissed( entry, row, controller, intent, isActivityIntent, animate, showOverLockscreen); + } + + @Override + public boolean willRunAnimationOnKeyguard() { + return animate; + } + }; if (showOverLockscreen) { mIsCollapsingToShowActivityOverLockscreen = true; postKeyguardAction.onDismiss(); @@ -453,53 +463,76 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit public void startNotificationGutsIntent(final Intent intent, final int appUid, ExpandableNotificationRow row) { boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */); - mActivityStarter.dismissKeyguardThenExecute(() -> { - AsyncTask.execute(() -> { - ActivityLaunchAnimator.Controller animationController = - new StatusBarLaunchAnimatorController( - mNotificationAnimationProvider.getAnimatorController(row), - mStatusBar, true /* isActivityIntent */); - - mActivityLaunchAnimator.startIntentWithAnimation( - animationController, animate, intent.getPackage(), - (adapter) -> TaskStackBuilder.create(mContext) - .addNextIntentWithParentStack(intent) - .startActivities(getActivityOptions( - mStatusBar.getDisplayId(), - adapter), - new UserHandle(UserHandle.getUserId(appUid)))); - }); - return true; - }, null, false /* afterKeyguardGone */); + ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { + @Override + public boolean onDismiss() { + AsyncTask.execute(() -> { + ActivityLaunchAnimator.Controller animationController = + new StatusBarLaunchAnimatorController( + mNotificationAnimationProvider.getAnimatorController(row), + mStatusBar, true /* isActivityIntent */); + + mActivityLaunchAnimator.startIntentWithAnimation( + animationController, animate, intent.getPackage(), + (adapter) -> TaskStackBuilder.create(mContext) + .addNextIntentWithParentStack(intent) + .startActivities(getActivityOptions( + mStatusBar.getDisplayId(), + adapter), + new UserHandle(UserHandle.getUserId(appUid)))); + }); + return true; + } + + @Override + public boolean willRunAnimationOnKeyguard() { + return animate; + } + }; + mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null, + false /* afterKeyguardGone */); } @Override public void startHistoryIntent(View view, boolean showHistory) { boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */); - mActivityStarter.dismissKeyguardThenExecute(() -> { - AsyncTask.execute(() -> { - Intent intent = showHistory ? new Intent( - Settings.ACTION_NOTIFICATION_HISTORY) : new Intent( - Settings.ACTION_NOTIFICATION_SETTINGS); - TaskStackBuilder tsb = TaskStackBuilder.create(mContext) - .addNextIntent(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS)); - if (showHistory) { - tsb.addNextIntent(intent); - } + ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { + @Override + public boolean onDismiss() { + AsyncTask.execute(() -> { + Intent intent = showHistory ? new Intent( + Settings.ACTION_NOTIFICATION_HISTORY) : new Intent( + Settings.ACTION_NOTIFICATION_SETTINGS); + TaskStackBuilder tsb = TaskStackBuilder.create(mContext) + .addNextIntent(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS)); + if (showHistory) { + tsb.addNextIntent(intent); + } - ActivityLaunchAnimator.Controller animationController = - new StatusBarLaunchAnimatorController( - ActivityLaunchAnimator.Controller.fromView(view), mStatusBar, - true /* isActivityIntent */); + ActivityLaunchAnimator.Controller viewController = + ActivityLaunchAnimator.Controller.fromView(view, + InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON + ); + ActivityLaunchAnimator.Controller animationController = + new StatusBarLaunchAnimatorController(viewController, mStatusBar, + true /* isActivityIntent */); + + mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, + intent.getPackage(), + (adapter) -> tsb.startActivities( + getActivityOptions(mStatusBar.getDisplayId(), adapter), + UserHandle.CURRENT)); + }); + return true; + } - mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, - intent.getPackage(), - (adapter) -> tsb.startActivities( - getActivityOptions(mStatusBar.getDisplayId(), adapter), - UserHandle.CURRENT)); - }); - return true; - }, null, false /* afterKeyguardGone */); + @Override + public boolean willRunAnimationOnKeyguard() { + return animate; + } + }; + mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null, + false /* afterKeyguardGone */); } private void removeHunAfterClick(ExpandableNotificationRow row) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index e135cc51a7bc..9a04d39c4e9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -3,6 +3,8 @@ package com.android.systemui.statusbar.phone import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator +import android.content.Context +import android.content.res.Configuration import android.os.Handler import android.view.View import com.android.systemui.animation.Interpolators @@ -16,6 +18,7 @@ import com.android.systemui.statusbar.notification.AnimatableProperty import com.android.systemui.statusbar.notification.PropertyAnimator import com.android.systemui.statusbar.notification.stack.AnimationProperties import com.android.systemui.statusbar.notification.stack.StackStateAnimator +import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject /** @@ -38,10 +41,11 @@ private const val LIGHT_REVEAL_ANIMATION_DURATION = 750L */ @SysUISingleton class UnlockedScreenOffAnimationController @Inject constructor( + private val context: Context, private val wakefulnessLifecycle: WakefulnessLifecycle, private val statusBarStateControllerImpl: StatusBarStateControllerImpl, private val keyguardViewMediatorLazy: dagger.Lazy<KeyguardViewMediator>, - private val dozeParameters: DozeParameters + private val keyguardStateController: KeyguardStateController ) : WakefulnessLifecycle.Observer { private val handler = Handler() @@ -131,13 +135,18 @@ class UnlockedScreenOffAnimationController @Inject constructor( lightRevealAnimationPlaying = false aodUiAnimationPlaying = false - // Make sure the status bar is in the correct keyguard state, since we might have left it in - // the KEYGUARD state if this wakeup cancelled the screen off animation. - statusBar.updateIsKeyguard() + // Make sure the status bar is in the correct keyguard state, forcing it if necessary. This + // is required if the screen off animation is cancelled, since it might be incorrectly left + // in the KEYGUARD or SHADE states depending on when it was cancelled and whether 'lock + // instantly' is enabled. We need to force it so that the state is set even if we're going + // from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have changed parts of the UI + // (such as showing AOD in the shade) without actually changing the StatusBarState. This + // ensures that the UI definitely reflects the desired state. + statusBar.updateIsKeyguard(true /* force */) } override fun onStartedGoingToSleep() { - if (shouldPlayScreenOffAnimation()) { + if (shouldPlayUnlockedScreenOffAnimation()) { lightRevealAnimationPlaying = true lightRevealAnimator.start() @@ -151,13 +160,32 @@ class UnlockedScreenOffAnimationController @Inject constructor( } /** - * Whether we should play the screen off animation when the phone starts going to sleep. We can - * do that if dozeParameters says we can control the unlocked screen off animation and we are in - * the SHADE state. If we're in KEYGUARD or SHADE_LOCKED, the regular + * Whether we want to play the screen off animation when the phone starts going to sleep, based + * on the current state of the device. */ - fun shouldPlayScreenOffAnimation(): Boolean { - return dozeParameters.shouldControlUnlockedScreenOff() && - statusBarStateControllerImpl.state == StatusBarState.SHADE + fun shouldPlayUnlockedScreenOffAnimation(): Boolean { + // We only play the unlocked screen off animation if we are... unlocked. + if (statusBarStateControllerImpl.state != StatusBarState.SHADE) { + return false + } + + // We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's + // already expanded and showing notifications/QS, the animation looks really messy. For now, + // disable it if the notification panel is expanded. + if (!this::statusBar.isInitialized || + statusBar.notificationPanelViewController.isFullyExpanded) { + return false + } + + // If we're not allowed to rotate the keyguard, then only do the screen off animation if + // we're in portrait. Otherwise, AOD will animate in sideways, which looks weird. + if (!keyguardStateController.isKeyguardScreenRotationAllowed && + context.resources.configuration.orientation != Configuration.ORIENTATION_PORTRAIT) { + return false + } + + // Otherwise, good to go. + return true } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index ef7fac311799..b295f6659f81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -25,6 +25,7 @@ import android.content.Intent import android.util.Log import android.view.View import android.widget.Chronometer +import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dagger.SysUISingleton @@ -179,7 +180,10 @@ class OngoingCallController @Inject constructor( logger.logChipClicked() activityStarter.postStartActivityDismissingKeyguard( currentCallNotificationInfo.intent, 0, - ActivityLaunchAnimator.Controller.fromView(backgroundView)) + ActivityLaunchAnimator.Controller.fromView( + backgroundView, + InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP) + ) } setUpUidObserver(currentCallNotificationInfo) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java index af7bf9500bf3..fcfc9670b8b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java @@ -57,6 +57,11 @@ public interface KeyguardStateController extends CallbackController<Callback> { boolean canPerformSmartSpaceTransition(); /** + * Whether the keyguard is allowed to rotate, or needs to be locked to the default orientation. + */ + boolean isKeyguardScreenRotationAllowed(); + + /** * If the device has PIN/pattern/password or a lock screen at all. */ boolean isMethodSecure(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index 0945a3f884d6..64750bd803d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.hardware.biometrics.BiometricSourceType; import android.os.Build; +import android.os.SystemProperties; import android.os.Trace; import androidx.annotation.VisibleForTesting; @@ -31,6 +32,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dumpable; +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; @@ -50,6 +52,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth"; private final ArrayList<Callback> mCallbacks = new ArrayList<>(); + private final Context mContext; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final LockPatternUtils mLockPatternUtils; private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = @@ -100,6 +103,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum public KeyguardStateControllerImpl(Context context, KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils, SmartspaceTransitionController smartspaceTransitionController) { + mContext = context; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); @@ -243,6 +247,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum } @Override + public boolean isKeyguardScreenRotationAllowed() { + return SystemProperties.getBoolean("lockscreen.rot_override", false) + || mContext.getResources().getBoolean(R.bool.config_enableLockScreenRotation); + } + + @Override public boolean isFaceAuthEnabled() { return mFaceAuthEnabled; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index efeeac669491..e6c4e82cec7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -336,7 +336,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent, results); - mEntry.remoteInputText = mEditText.getText(); + mEntry.remoteInputText = mEditText.getText().toString(); // TODO(b/188646667): store attachment to entry mEntry.remoteInputUri = null; mEntry.remoteInputMimeType = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 5c440173e547..1ebb9ddc0262 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -21,6 +21,7 @@ import static android.os.UserManager.SWITCHABILITY_STATUS_OK; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import static com.android.systemui.DejankUtils.whitelistIpcs; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.Dialog; import android.app.IActivityTaskManager; @@ -48,6 +49,7 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManagerGlobal; import android.widget.BaseAdapter; import com.android.internal.annotations.VisibleForTesting; @@ -63,6 +65,7 @@ import com.android.systemui.SystemUISecondaryUserService; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.qs.QSUserSwitcherEvent; @@ -76,6 +79,8 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; import javax.inject.Provider; @@ -123,23 +128,35 @@ public class UserSwitcherController implements Dumpable { private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2); private final UiEventLogger mUiEventLogger; public final DetailAdapter mUserDetailAdapter; + private final Executor mUiBgExecutor; + private final boolean mGuestUserAutoCreated; + private final AtomicBoolean mGuestCreationScheduled; @Inject - public UserSwitcherController(Context context, KeyguardStateController keyguardStateController, - @Main Handler handler, ActivityStarter activityStarter, - BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger, + public UserSwitcherController(Context context, + KeyguardStateController keyguardStateController, + @Main Handler handler, + ActivityStarter activityStarter, + BroadcastDispatcher broadcastDispatcher, + UiEventLogger uiEventLogger, TelephonyListenerManager telephonyListenerManager, - IActivityTaskManager activityTaskManager, UserDetailAdapter userDetailAdapter) { + IActivityTaskManager activityTaskManager, + UserDetailAdapter userDetailAdapter, + @UiBackground Executor uiBgExecutor) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mTelephonyListenerManager = telephonyListenerManager; mActivityTaskManager = activityTaskManager; mUiEventLogger = uiEventLogger; - mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(mUiEventLogger); + mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(this, mUiEventLogger); mUserDetailAdapter = userDetailAdapter; + mUiBgExecutor = uiBgExecutor; if (!UserManager.isGuestUserEphemeral()) { mGuestResumeSessionReceiver.register(mBroadcastDispatcher); } + mGuestUserAutoCreated = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_guestUserAutoCreated); + mGuestCreationScheduled = new AtomicBoolean(); mKeyguardStateController = keyguardStateController; mHandler = handler; mActivityStarter = activityStarter; @@ -379,21 +396,13 @@ public class UserSwitcherController implements Dumpable { int id; if (record.isGuest && record.info == null) { // No guest user. Create one. - UserInfo guest; - try { - guest = mUserManager.createGuest(mContext, - mContext.getString(com.android.settingslib.R.string.guest_nickname)); - } catch (UserManager.UserOperationException e) { - Log.e(TAG, "Couldn't create guest user", e); - return; - } - if (guest == null) { - // Couldn't create guest, most likely because there already exists one, we just - // haven't reloaded the user list yet. + int guestId = createGuest(); + if (guestId == UserHandle.USER_NULL) { + // This may happen if we haven't reloaded the user list yet. return; } mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD); - id = guest.id; + id = guestId; } else if (record.isAddUser) { showAddUserDialog(); return; @@ -457,11 +466,6 @@ public class UserSwitcherController implements Dumpable { mAddUserDialog.show(); } - protected void exitGuest(int id, int targetId) { - switchToUserId(targetId); - mUserManager.removeUser(id); - } - private void listenForCallState() { mTelephonyListenerManager.addCallStateListener(mPhoneStateListener); } @@ -576,6 +580,7 @@ public class UserSwitcherController implements Dumpable { pw.print(" "); pw.println(u.toString()); } pw.println("mSimpleUserSwitcher=" + mSimpleUserSwitcher); + pw.println("mGuestUserAutoCreated=" + mGuestUserAutoCreated); } /** Returns the name of the current user of the phone. */ @@ -602,6 +607,126 @@ public class UserSwitcherController implements Dumpable { return mUsers; } + /** + * Removes guest user and switches to target user. The guest must be the current user and its id + * must be {@code guestUserId}. + * + * <p>If {@code targetUserId} is {@link UserHandle.USER_NULL}, then create a new guest user in + * the foreground, and immediately switch to it. This is used for wiping the current guest and + * replacing it with a new one. + * + * <p>If {@code targetUserId} is specified, then remove the guest in the background while + * switching to {@code targetUserId}. + * + * <p>If device is configured with {@link + * com.android.internal.R.bool.config_guestUserAutoCreated}, then after guest user is removed, a + * new one is created in the background. This has no effect if {@code targetUserId} is {@link + * UserHandle.USER_NULL}. + * + * @param guestUserId id of the guest user to remove + * @param targetUserId id of the user to switch to after guest is removed. If {@link + * UserHandle.USER_NULL}, then switch immediately to the newly created guest user. + */ + public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) { + UserInfo currentUser; + try { + currentUser = ActivityManager.getService().getCurrentUser(); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't remove guest because ActivityManager is dead"); + return; + } + if (currentUser.id != guestUserId) { + Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")" + + " is not current user (" + currentUser.id + ")"); + return; + } + if (!currentUser.isGuest()) { + Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")" + + " is not a guest"); + return; + } + + boolean marked = mUserManager.markGuestForDeletion(currentUser.id); + if (!marked) { + Log.w(TAG, "Couldn't mark the guest for deletion for user " + guestUserId); + return; + } + + try { + if (targetUserId == UserHandle.USER_NULL) { + // Create a new guest in the foreground, and then immediately switch to it + int newGuestId = createGuest(); + if (newGuestId == UserHandle.USER_NULL) { + Log.e(TAG, "Could not create new guest, switching back to system user"); + switchToUserId(UserHandle.USER_SYSTEM); + mUserManager.removeUser(currentUser.id); + WindowManagerGlobal.getWindowManagerService().lockNow(/* options= */ null); + return; + } + switchToUserId(newGuestId); + mUserManager.removeUser(currentUser.id); + } else { + if (mGuestUserAutoCreated) { + // TODO(b/191067027): Move guest recreation to system_server + scheduleGuestCreation(); + } + switchToUserId(targetUserId); + } + } catch (RemoteException e) { + Log.e(TAG, "Couldn't remove guest because ActivityManager or WindowManager is dead"); + return; + } + } + + private void scheduleGuestCreation() { + if (!mGuestCreationScheduled.compareAndSet(false, true)) { + return; + } + + mUiBgExecutor.execute(() -> { + int newGuestId = createGuest(); + if (newGuestId == UserHandle.USER_NULL) { + Log.w(TAG, "Could not create new guest while exiting existing guest"); + } + mGuestCreationScheduled.set(false); + }); + + } + + /** + * If there is no guest on the device, schedule creation of a new guest user in the background. + */ + public void guaranteeGuestPresent() { + if (mUserManager.findCurrentGuestUser() == null) { + scheduleGuestCreation(); + } + } + + /** + * Creates a guest user and return its multi-user user ID. + * + * This method does not check if a guest already exists before it makes a call to + * {@link UserManager} to create a new one. + * + * @return The multi-user user ID of the newly created guest user, or + * {@link UserHandle.USER_NULL} if the guest couldn't be created. + */ + public @UserIdInt int createGuest() { + UserInfo guest; + try { + guest = mUserManager.createGuest(mContext, + mContext.getString(com.android.settingslib.R.string.guest_nickname)); + } catch (UserManager.UserOperationException e) { + Log.e(TAG, "Couldn't create guest user", e); + return UserHandle.USER_NULL; + } + if (guest == null) { + Log.e(TAG, "Couldn't create guest, most likely because there already exists one"); + return UserHandle.USER_NULL; + } + return guest.id; + } + public static abstract class BaseUserAdapter extends BaseAdapter { final UserSwitcherController mController; @@ -662,10 +787,15 @@ public class UserSwitcherController implements Dumpable { public String getName(Context context, UserRecord item) { if (item.isGuest) { if (item.isCurrent) { - return context.getString(com.android.settingslib.R.string.guest_exit_guest); + return context.getString(mController.mGuestUserAutoCreated + ? com.android.settingslib.R.string.guest_reset_guest + : com.android.settingslib.R.string.guest_exit_guest); } else { + // If config_guestUserAutoCreated, always show guest nickname instead of "Add + // guest" to make it seem as though the device always has a guest ready for use return context.getString( - item.info == null ? com.android.settingslib.R.string.guest_new_guest + item.info == null && !mController.mGuestUserAutoCreated + ? com.android.settingslib.R.string.guest_new_guest : com.android.settingslib.R.string.guest_nickname); } } else if (item.isAddUser) { @@ -882,12 +1012,15 @@ public class UserSwitcherController implements Dumpable { public ExitGuestDialog(Context context, int guestId, int targetId) { super(context); - setTitle(R.string.guest_exit_guest_dialog_title); + setTitle(mGuestUserAutoCreated ? R.string.guest_reset_guest_dialog_title + : R.string.guest_exit_guest_dialog_title); setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), this); setButton(DialogInterface.BUTTON_POSITIVE, - context.getString(R.string.guest_exit_guest_dialog_remove), this); + context.getString( + mGuestUserAutoCreated ? R.string.guest_reset_guest_dialog_remove + : R.string.guest_exit_guest_dialog_remove), this); SystemUIDialog.setWindowOnTop(this); setCanceledOnTouchOutside(false); mGuestId = guestId; @@ -901,7 +1034,7 @@ public class UserSwitcherController implements Dumpable { } else { mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); dismiss(); - exitGuest(mGuestId, mTargetId); + removeGuestUser(mGuestId, mTargetId); } } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 26f4a2b9d2b2..d97815f92964 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -68,7 +68,8 @@ public class TunerServiceImpl extends TunerService { private static final String[] RESET_EXCEPTION_LIST = new String[] { QSTileHost.TILES_SETTING, Settings.Secure.DOZE_ALWAYS_ON, - Settings.Secure.MEDIA_CONTROLS_RESUME + Settings.Secure.MEDIA_CONTROLS_RESUME, + Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION }; private final Observer mObserver = new Observer(); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index a5ccc47b8072..ab4b1f10132c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -135,7 +135,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private boolean mShowVolumeDialog; private boolean mShowSafetyWarning; private long mLastToggledRingerOn; - private boolean mDeviceInteractive; + private boolean mDeviceInteractive = true; private boolean mDestroyed; private VolumePolicy mVolumePolicy; diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java index 8412a8ae966e..0c5347724035 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java @@ -104,15 +104,14 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard float percentDistanceFromCenter) { CharSequence centerCardText = getLabelText(centerCard); Drawable centerCardIcon = getHeaderIcon(mContext, centerCard); - if (!TextUtils.equals(mCenterCardText, centerCardText)) { - mCenterCardText = centerCardText; - mCardLabel.setText(centerCardText); - mIcon.setImageDrawable(centerCardIcon); - } renderActionButton(centerCard, mIsDeviceLocked, mIsUdfpsEnabled); - if (TextUtils.equals(centerCardText, getLabelText(nextCard))) { + if (centerCard.isUiEquivalent(nextCard)) { mCardLabel.setAlpha(1f); + mIcon.setAlpha(1f); + mActionButton.setAlpha(1f); } else { + mCardLabel.setText(centerCardText); + mIcon.setImageDrawable(centerCardIcon); mCardLabel.setAlpha(percentDistanceFromCenter); mIcon.setAlpha(percentDistanceFromCenter); mActionButton.setAlpha(percentDistanceFromCenter); @@ -141,6 +140,7 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard mErrorView.setVisibility(GONE); mEmptyStateView.setVisibility(GONE); mIcon.setImageDrawable(getHeaderIcon(mContext, data.get(selectedIndex))); + mCardLabel.setText(getLabelText(data.get(selectedIndex))); renderActionButton(data.get(selectedIndex), isDeviceLocked, mIsUdfpsEnabled); if (shouldAnimate) { animateViewsShown(mIcon, mCardLabel, mActionButton); @@ -248,20 +248,20 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard private void renderActionButton( WalletCardViewInfo walletCard, boolean isDeviceLocked, boolean isUdfpsEnabled) { CharSequence actionButtonText = getActionButtonText(walletCard); - if (!isUdfpsEnabled && isDeviceLocked) { + if (!isUdfpsEnabled && actionButtonText != null) { mActionButton.setVisibility(VISIBLE); - mActionButton.setText(R.string.wallet_action_button_label_unlock); - mActionButton.setOnClickListener(mDeviceLockedActionOnClickListener); - } else if (!isDeviceLocked && actionButtonText != null) { mActionButton.setText(actionButtonText); - mActionButton.setVisibility(VISIBLE); - mActionButton.setOnClickListener(v -> { - try { - walletCard.getPendingIntent().send(); - } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "Error sending pending intent for wallet card"); - } - }); + mActionButton.setOnClickListener( + isDeviceLocked + ? mDeviceLockedActionOnClickListener + : v -> { + try { + walletCard.getPendingIntent().send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Error sending pending intent for wallet card."); + } + } + ); } else { mActionButton.setVisibility(GONE); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index 5441bd4c958d..a29a638b91a8 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -617,7 +617,7 @@ public class BubblesManager implements Dumpable { * cancel it (and hence the bubbles associated with it). * * @return true if we want to intercept the dismissal of the entry, else false. - * @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer) + * @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer, Executor) */ public boolean handleDismissalInterception(NotificationEntry entry) { if (entry == null) { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 92ef8504d123..bc956dc85702 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -34,6 +34,7 @@ import android.graphics.drawable.Drawable; import android.inputmethodservice.InputMethodService; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.view.KeyEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; @@ -44,6 +45,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.shared.tracing.ProtoTraceable; @@ -57,6 +59,7 @@ import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; +import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.onehanded.OneHandedUiEventLogger; import com.android.wm.shell.pip.Pip; @@ -112,6 +115,7 @@ public final class WMShell extends SystemUI private final NavigationModeController mNavigationModeController; private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; + private final WakefulnessLifecycle mWakefulnessLifecycle; private final ProtoTracer mProtoTracer; private final Executor mSysUiMainExecutor; @@ -119,6 +123,7 @@ public final class WMShell extends SystemUI private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; private KeyguardUpdateMonitorCallback mPipKeyguardCallback; private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback; + private WakefulnessLifecycle.Observer mWakefulnessObserver; @Inject public WMShell(Context context, @@ -134,6 +139,7 @@ public final class WMShell extends SystemUI ScreenLifecycle screenLifecycle, SysUiState sysUiState, ProtoTracer protoTracer, + WakefulnessLifecycle wakefulnessLifecycle, @Main Executor sysUiMainExecutor) { super(context); mCommandQueue = commandQueue; @@ -146,6 +152,7 @@ public final class WMShell extends SystemUI mSplitScreenOptional = splitScreenOptional; mOneHandedOptional = oneHandedOptional; mHideDisplayCutoutOptional = hideDisplayCutoutOptional; + mWakefulnessLifecycle = wakefulnessLifecycle; mProtoTracer = protoTracer; mShellCommandHandler = shellCommandHandler; mSysUiMainExecutor = sysUiMainExecutor; @@ -253,23 +260,19 @@ public final class WMShell extends SystemUI } }); - mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() { + oneHanded.registerEventCallback(new OneHandedEventCallback() { @Override - public void onKeyguardBouncerChanged(boolean bouncer) { - if (bouncer) { - oneHanded.stopOneHanded(); - } + public void notifyExpandNotification() { + mSysUiMainExecutor.execute( + () -> mCommandQueue.handleSystemKey( + KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)); } + }); + mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() { @Override public void onKeyguardVisibilityChanged(boolean showing) { - if (showing) { - // When keyguard shown, temperory lock OHM disabled to avoid mis-trigger. - oneHanded.setLockedDisabled(true /* locked */, false /* enabled */); - } else { - // Reset locked. - oneHanded.setLockedDisabled(false /* locked */, false /* enabled */); - } + oneHanded.onKeyguardVisibilityChanged(showing); oneHanded.stopOneHanded(); } @@ -280,6 +283,24 @@ public final class WMShell extends SystemUI }; mKeyguardUpdateMonitor.registerCallback(mOneHandedKeyguardCallback); + mWakefulnessObserver = + new WakefulnessLifecycle.Observer() { + @Override + public void onFinishedWakingUp() { + // Reset locked for the case keyguard not shown. + oneHanded.setLockedDisabled(false /* locked */, false /* enabled */); + } + + @Override + public void onStartedGoingToSleep() { + oneHanded.stopOneHanded(); + // When user press power button going to sleep, temperory lock OHM disabled + // to avoid mis-trigger. + oneHanded.setLockedDisabled(true /* locked */, false /* enabled */); + } + }; + mWakefulnessLifecycle.addObserver(mWakefulnessObserver); + mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() { @Override public void onScreenTurningOff() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index 485df21656d3..5617f1b6316b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -59,7 +59,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.MathUtils; import android.view.Choreographer; import android.view.MotionEvent; import android.view.View; @@ -489,20 +488,20 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void onRotationChanged_buttonIsShowing_expectedYPosition() { final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); - final int oldWindowHeight = windowBounds.height(); mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds); final float windowHeightFraction = - (float) mWindowManager.getLayoutParamsFromAttachedView().y / oldWindowHeight; + (float) (mWindowManager.getLayoutParamsFromAttachedView().y + - oldDraggableBounds.top) / oldDraggableBounds.height(); - // The window bounds are changed due to the rotation change. + // The window bounds and the draggable bounds are changed due to the rotation change. final Rect newWindowBounds = new Rect(0, 0, windowBounds.height(), windowBounds.width()); mWindowManager.setWindowBounds(newWindowBounds); mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); - int expectedY = (int) (newWindowBounds.height() * windowHeightFraction); - expectedY = MathUtils.constrain(expectedY, - mMagnificationModeSwitch.mDraggableWindowBounds.top, - mMagnificationModeSwitch.mDraggableWindowBounds.bottom); + int expectedY = (int) (windowHeightFraction + * mMagnificationModeSwitch.mDraggableWindowBounds.height()) + + mMagnificationModeSwitch.mDraggableWindowBounds.top; assertEquals( "The Y position does not keep the same height ratio after the rotation changed.", expectedY, mWindowManager.getLayoutParamsFromAttachedView().y); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index 240fdf3a4e17..d87a26b096fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -25,6 +25,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.StatusBar import com.android.systemui.statusbar.policy.ConfigurationController @@ -53,6 +54,7 @@ class AuthRippleControllerTest : SysuiTestCase() { @Mock private lateinit var authController: AuthController @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController @Mock private lateinit var bypassController: KeyguardBypassController + @Mock private lateinit var biometricUnlockController: BiometricUnlockController @Before fun setUp() { @@ -66,6 +68,7 @@ class AuthRippleControllerTest : SysuiTestCase() { commandRegistry, notificationShadeWindowController, bypassController, + biometricUnlockController, rippleView ) controller.init() @@ -90,7 +93,7 @@ class AuthRippleControllerTest : SysuiTestCase() { // THEN update sensor location and show ripple verify(rippleView).setSensorLocation(fpsLocation) - verify(rippleView).startRipple(any()) + verify(rippleView).startRipple(any(), any()) } @Test @@ -111,7 +114,7 @@ class AuthRippleControllerTest : SysuiTestCase() { false /* isStrongBiometric */) // THEN no ripple - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test @@ -132,7 +135,7 @@ class AuthRippleControllerTest : SysuiTestCase() { false /* isStrongBiometric */) // THEN no ripple - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test @@ -156,7 +159,7 @@ class AuthRippleControllerTest : SysuiTestCase() { // THEN show ripple verify(rippleView).setSensorLocation(faceLocation) - verify(rippleView).startRipple(any()) + verify(rippleView).startRipple(any(), any()) } @Test @@ -176,7 +179,7 @@ class AuthRippleControllerTest : SysuiTestCase() { false /* isStrongBiometric */) // THEN no ripple - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test @@ -191,7 +194,7 @@ class AuthRippleControllerTest : SysuiTestCase() { 0 /* userId */, BiometricSourceType.FACE /* type */, false /* isStrongBiometric */) - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test @@ -206,7 +209,7 @@ class AuthRippleControllerTest : SysuiTestCase() { 0 /* userId */, BiometricSourceType.FINGERPRINT /* type */, false /* isStrongBiometric */) - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index c0b45c6d5c96..10997fab081f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -16,7 +16,10 @@ package com.android.systemui.doze; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; + import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; @@ -161,7 +164,7 @@ public class DozeTriggersTest extends SysuiTestCase { clearInvocations(mSensors); mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE); - mTriggers.transitionTo(DozeMachine.State.DOZE_PULSE_DONE, DozeMachine.State.DOZE_AOD); + mTriggers.transitionTo(DozeMachine.State.DOZE_PULSE_DONE, DOZE_AOD); waitForSensorManager(); verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor)); } @@ -206,34 +209,17 @@ public class DozeTriggersTest extends SysuiTestCase { // WHEN quick pick up is triggered mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null); - // THEN device goes into aod (shows clock with black background) - verify(mMachine).requestState(DozeMachine.State.DOZE_AOD); + // THEN request pulse + verify(mMachine).requestPulse(anyInt()); // THEN a log is taken that quick pick up was triggered verify(mUiEventLogger).log(DozingUpdateUiEvent.DOZING_UPDATE_QUICK_PICKUP); } @Test - public void testQuickPickupTimeOutAfterExecutables() { - // GIVEN quick pickup is triggered when device is in DOZE - when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); - mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null); - verify(mMachine).requestState(DozeMachine.State.DOZE_AOD); - verify(mMachine, never()).requestState(DozeMachine.State.DOZE); - - // WHEN next executable is run - mExecutor.advanceClockToLast(); - mExecutor.runAllReady(); - - // THEN device goes back into DOZE - verify(mMachine).requestState(DozeMachine.State.DOZE); - - // THEN a log is taken that wake up timeout expired - verify(mUiEventLogger).log(DozingUpdateUiEvent.DOZING_UPDATE_WAKE_TIMEOUT); - } - - @Test public void testOnSensor_Fingerprint() { + // GIVEN dozing state + when(mMachine.getState()).thenReturn(DOZE_AOD); final int screenX = 100; final int screenY = 100; final float misc = -1; @@ -241,8 +227,20 @@ public class DozeTriggersTest extends SysuiTestCase { final float major = 3f; final int reason = DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; float[] rawValues = new float[]{screenX, screenY, misc, major, minor}; + + // WHEN longpress gesture is triggered mTriggers.onSensor(reason, screenX, screenY, rawValues); + + // THEN + // * don't immediately send interrupt + // * immediately extend pulse + verify(mAuthController, never()).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat()); verify(mHost).extendPulse(reason); + + // WHEN display state changes to ON + mTriggers.onScreenState(Display.STATE_ON); + + // THEN send interrupt verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY), eq(major), eq(minor)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java index afe5c0b2edbd..1d34aac3b1cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java @@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.wakelock.WakeLockFake; @@ -77,6 +78,8 @@ public class DozeUiTest extends SysuiTestCase { private DozeUi mDozeUi; @Mock private StatusBarStateController mStatusBarStateController; + @Mock + private ConfigurationController mConfigurationController; @Before public void setUp() throws Exception { @@ -89,7 +92,7 @@ public class DozeUiTest extends SysuiTestCase { mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService, - () -> mStatusBarStateController); + () -> mStatusBarStateController, mConfigurationController); mDozeUi.setDozeMachine(mMachine); } @@ -146,7 +149,7 @@ public class DozeUiTest extends SysuiTestCase { when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true); mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService, - () -> mStatusBarStateController); + () -> mStatusBarStateController, mConfigurationController); mDozeUi.setDozeMachine(mMachine); // Never animate if display doesn't support it. diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index 83e7b17eb746..adc8ffc1d633 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.when; import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Color; import android.media.AudioManager; @@ -38,8 +39,9 @@ import android.os.UserManager; import android.service.dreams.IDreamManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.GestureDetector; import android.view.IWindowManager; -import android.view.View; +import android.view.MotionEvent; import android.view.WindowManagerPolicyConstants; import androidx.test.filters.SmallTest; @@ -57,6 +59,7 @@ import com.android.systemui.plugins.GlobalActions; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.telephony.TelephonyListenerManager; @@ -107,8 +110,10 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private RingerModeTracker mRingerModeTracker; @Mock private RingerModeLiveData mRingerModeLiveData; @Mock private SysUiState mSysUiState; + @Mock private PackageManager mPackageManager; @Mock private Handler mHandler; @Mock private UserContextProvider mUserContextProvider; + @Mock private StatusBar mStatusBar; private TestableLooper mTestableLooper; @@ -120,6 +125,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); when(mUserContextProvider.getUserContext()).thenReturn(mContext); + when(mResources.getConfiguration()).thenReturn( + getContext().getResources().getConfiguration()); mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext, mWindowManagerFuncs, @@ -150,7 +157,9 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mInfoProvider, mRingerModeTracker, mSysUiState, - mHandler + mHandler, + mPackageManager, + mStatusBar ); mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting(); @@ -194,7 +203,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { } @Test - public void testShouldLogOnTapOutside() { + public void testSingleTap_logAndDismiss() { mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); @@ -207,9 +216,58 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { }; doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog(); - View container = dialog.findViewById(com.android.systemui.R.id.global_actions_container); - container.callOnClick(); + + GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener); + gestureListener.onSingleTapConfirmed(null); + verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); + } + + @Test + public void testSwipeDownLockscreen_logAndOpenQS() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any()); + doReturn(true).when(mStatusBar).isKeyguardShowing(); + String[] actions = { + GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART, + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog(); + + GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener); + MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); + MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0); + gestureListener.onFling(start, end, 0, 1000); + verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); + verify(mStatusBar).animateExpandSettingsPanel(null); + } + + @Test + public void testSwipeDown_logAndOpenNotificationShade() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any()); + doReturn(false).when(mStatusBar).isKeyguardShowing(); + String[] actions = { + GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART, + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog(); + + GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener); + MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); + MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0); + gestureListener.onFling(start, end, 0, 1000); verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); + verify(mStatusBar).animateExpandNotificationsPanel(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index 3130e977dc83..e5c104e7d377 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.when; import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Color; @@ -65,6 +66,7 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.telephony.TelephonyListenerManager; @@ -123,7 +125,9 @@ public class GlobalActionsDialogTest extends SysuiTestCase { @Mock GlobalActionsPanelPlugin.PanelViewController mWalletController; @Mock private Handler mHandler; @Mock private UserTracker mUserTracker; + @Mock private PackageManager mPackageManager; @Mock private SecureSettings mSecureSettings; + @Mock private StatusBar mStatusBar; private TestableLooper mTestableLooper; @@ -134,6 +138,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase { allowTestableLooperAsMainThread(); when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); + when(mResources.getConfiguration()).thenReturn( + getContext().getResources().getConfiguration()); mGlobalActionsDialog = new GlobalActionsDialog(mContext, mWindowManagerFuncs, @@ -164,7 +170,9 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mUiEventLogger, mRingerModeTracker, mSysUiState, - mHandler + mHandler, + mPackageManager, + mStatusBar ); mGlobalActionsDialog.setZeroDialogPressDelayForTesting(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java index 2f78532b9e71..51576687880c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java @@ -34,7 +34,6 @@ import android.content.res.ColorStateList; import android.graphics.Color; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; -import android.view.View; import androidx.test.filters.SmallTest; @@ -258,8 +257,8 @@ public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCas // WHEN the device is dozing mStatusBarStateListener.onDozingChanged(true); - // THEN the view is GONE - verify(mView).setVisibility(View.GONE); + // THEN switch to INDICATION_TYPE_NONE + verify(mView).switchIndication(null); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index d9b56a49f12a..1dacc6245d84 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -52,6 +52,7 @@ import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.concurrency.FakeExecutor; @@ -78,6 +79,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock DumpManager mDumpManager; private @Mock PowerManager mPowerManager; private @Mock TrustManager mTrustManager; + private @Mock UserSwitcherController mUserSwitcherController; private @Mock NavigationModeController mNavigationModeController; private @Mock KeyguardDisplayManager mKeyguardDisplayManager; private @Mock DozeParameters mDozeParameters; @@ -100,13 +102,27 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class)); mViewMediator = new KeyguardViewMediator( - mContext, mFalsingCollector, mLockPatternUtils, mBroadcastDispatcher, + mContext, + mFalsingCollector, + mLockPatternUtils, + mBroadcastDispatcher, () -> mStatusBarKeyguardViewManager, - mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor, - mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController, - mKeyguardDisplayManager, mDozeParameters, mStatusBarStateController, - mKeyguardStateController, () -> mKeyguardUnlockAnimationController, - mUnlockedScreenOffAnimationController, () -> mNotificationShadeDepthController); + mDismissCallbackRegistry, + mUpdateMonitor, + mDumpManager, + mUiBgExecutor, + mPowerManager, + mTrustManager, + mUserSwitcherController, + mDeviceConfig, + mNavigationModeController, + mKeyguardDisplayManager, + mDozeParameters, + mStatusBarStateController, + mKeyguardStateController, + () -> mKeyguardUnlockAnimationController, + mUnlockedScreenOffAnimationController, + () -> mNotificationShadeDepthController); mViewMediator.start(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java index 63ce98a170a6..910b38105332 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java @@ -56,7 +56,7 @@ public class WakefulnessLifecycleTest extends SysuiTestCase { @Test public void baseState() throws Exception { - assertEquals(WakefulnessLifecycle.WAKEFULNESS_ASLEEP, mWakefulness.getWakefulness()); + assertEquals(WakefulnessLifecycle.WAKEFULNESS_AWAKE, mWakefulness.getWakefulness()); verifyNoMoreInteractions(mWakefulnessObserver); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index e20b426907be..66b64708ad24 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -82,9 +82,11 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void eventNotEmittedWithoutDevice() { // WHEN data source emits an event without device data - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN an event isn't emitted - verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean()); + verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), + anyBoolean()); } @Test @@ -92,7 +94,8 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { // WHEN device source emits an event without media data mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN an event isn't emitted - verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean()); + verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), + anyBoolean()); } @Test @@ -100,80 +103,95 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { // GIVEN that a device event has already been received mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN media event is received - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void emitEventAfterMediaFirst() { // GIVEN that media event has already been received - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); // WHEN device event is received mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void migrateKeyMediaFirst() { // GIVEN that media and device info has already been received - mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received - mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void migrateKeyDeviceFirst() { // GIVEN that media and device info has already been received - mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void migrateKeyMediaAfter() { // GIVEN that media and device info has already been received - mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); reset(mListener); // WHEN a second key migration event is received for media - mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN the key has already been migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void migrateKeyDeviceAfter() { // GIVEN that media and device info has already been received - mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); - mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */, + false /* isSsReactivated */); reset(mListener); // WHEN a second key migration event is received for the device mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the key has already be migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @@ -187,7 +205,8 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void mediaDataRemovedAfterMediaEvent() { - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @@ -202,13 +221,15 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void mediaDataKeyUpdated() { // GIVEN that device and media events have already been received - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN the key is changed - mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN the listener gets a load event with the correct keys ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded( - eq("NEW_KEY"), any(), captor.capture(), anyBoolean()); + eq("NEW_KEY"), any(), captor.capture(), anyBoolean(), anyBoolean()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt index 17f2a07eb249..d8791867cb45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt @@ -120,7 +120,8 @@ class MediaDataFilterTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) // THEN we should tell the listener - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), + eq(false)) } @Test @@ -129,7 +130,7 @@ class MediaDataFilterTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest) // THEN we should NOT tell the listener - verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean()) + verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean()) } @Test @@ -175,10 +176,12 @@ class MediaDataFilterTest : SysuiTestCase() { setUser(USER_GUEST) // THEN we should add back the guest user media - verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), + eq(false)) // but not the main user's - verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean()) + verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(), + anyBoolean()) } @Test @@ -245,7 +248,7 @@ class MediaDataFilterTest : SysuiTestCase() { mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) - verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean()) + verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean()) verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) assertThat(mediaDataFilter.hasActiveMedia()).isFalse() } @@ -282,12 +285,15 @@ class MediaDataFilterTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), + eq(false)) // AND we get a smartspace signal mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) - // THEN we should tell listeners to treat the media as active instead + // THEN we should tell listeners to treat the media as not active instead + verify(listener, never()).onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), + anyBoolean()) verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) assertThat(mediaDataFilter.hasActiveMedia()).isFalse() } @@ -299,14 +305,16 @@ class MediaDataFilterTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), + eq(false)) // AND we get a smartspace signal mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) // THEN we should tell listeners to treat the media as active instead val dataCurrentAndActive = dataCurrent.copy(active = true) - verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true), + eq(true)) assertThat(mediaDataFilter.hasActiveMedia()).isTrue() // Smartspace update shouldn't be propagated for the empty rec list. verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) @@ -317,14 +325,16 @@ class MediaDataFilterTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), + eq(false)) // AND we get a smartspace signal mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) // THEN we should tell listeners to treat the media as active instead val dataCurrentAndActive = dataCurrent.copy(active = true) - verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true), + eq(true)) assertThat(mediaDataFilter.hasActiveMedia()).isTrue() // Smartspace update should also be propagated but not prioritized. verify(listener) @@ -344,11 +354,17 @@ class MediaDataFilterTest : SysuiTestCase() { fun testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() { val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), + eq(false)) + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + val dataCurrentAndActive = dataCurrent.copy(active = true) + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true), + eq(true)) + mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) - verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrent), eq(true)) verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) assertThat(mediaDataFilter.hasActiveMedia()).isFalse() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 15cfee828293..5b4e12463370 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -9,8 +9,8 @@ import android.media.MediaDescription import android.media.MediaMetadata import android.media.session.MediaController import android.media.session.MediaSession -import android.provider.Settings import android.os.Bundle +import android.provider.Settings import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper @@ -20,6 +20,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.SbnBuilder +import com.android.systemui.tuner.TunerService import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq @@ -86,6 +87,8 @@ class MediaDataManagerTest : SysuiTestCase() { lateinit var mediaNotification: StatusBarNotification @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData> private val clock = FakeSystemClock() + @Mock private lateinit var tunerService: TunerService + @Captor lateinit var tunableCaptor: ArgumentCaptor<TunerService.Tunable> private val originalSmartspaceSetting = Settings.Secure.getInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1) @@ -114,8 +117,11 @@ class MediaDataManagerTest : SysuiTestCase() { smartspaceMediaDataProvider = smartspaceMediaDataProvider, useMediaResumption = true, useQsMediaPlayer = true, - systemClock = clock + systemClock = clock, + tunerService = tunerService ) + verify(tunerService).addTunable(capture(tunableCaptor), + eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION)) session = MediaSession(context, "MediaDataManagerTestSession") mediaNotification = SbnBuilder().run { setPkg(PACKAGE_NAME) @@ -179,7 +185,8 @@ class MediaDataManagerTest : SysuiTestCase() { fun testOnMetaDataLoaded_callsListener() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true), + eq(false)) } @Test @@ -190,7 +197,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value!!.active).isTrue() } @@ -209,7 +217,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) @@ -217,7 +226,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationRemoved(KEY) // THEN the media data indicates that it is for resumption verify(listener) - .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.resumption).isTrue() } @@ -230,7 +240,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(backgroundExecutor.runAllReady()).isEqualTo(2) assertThat(foregroundExecutor.runAllReady()).isEqualTo(2) verify(listener) - .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() val resumableData = data.copy(resumeAction = Runnable {}) @@ -241,7 +252,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationRemoved(KEY) // THEN the data is for resumption and the key is migrated to the package name verify(listener) - .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.resumption).isTrue() verify(listener, never()).onMediaDataRemoved(eq(KEY)) // WHEN the second is removed @@ -249,7 +261,8 @@ class MediaDataManagerTest : SysuiTestCase() { // THEN the data is for resumption and the second key is removed verify(listener) .onMediaDataLoaded( - eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true)) + eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.resumption).isTrue() verify(listener).onMediaDataRemoved(eq(KEY_2)) } @@ -263,7 +276,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value val dataRemoteWithResume = data.copy(resumeAction = Runnable {}, isLocalSession = false) mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume) @@ -289,7 +303,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) // THEN the media data indicates that it is for resumption verify(listener) - .onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value assertThat(data.resumption).isTrue() assertThat(data.song).isEqualTo(SESSION_TITLE) @@ -329,7 +344,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) verify(listener) - .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) } @Test @@ -364,6 +380,9 @@ class MediaDataManagerTest : SysuiTestCase() { fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_callsRemoveListener() { smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget)) smartspaceMediaDataProvider.onTargetsAvailable(listOf()) + foregroundExecutor.advanceClockToLast() + foregroundExecutor.runAllReady() + verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(false)) } @@ -372,6 +391,8 @@ class MediaDataManagerTest : SysuiTestCase() { // WHEN media recommendation setting is off Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0) + tunableCaptor.value.onTuningChanged(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, "0") + smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget)) // THEN smartspace signal is ignored @@ -380,12 +401,31 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testMediaRecommendationDisabled_removesSmartspaceData() { + // GIVEN a media recommendation card is present + smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget)) + verify(listener).onSmartspaceMediaDataLoaded(eq(KEY_MEDIA_SMARTSPACE), anyObject(), + anyBoolean()) + + // WHEN the media recommendation setting is turned off + Settings.Secure.putInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0) + tunableCaptor.value.onTuningChanged(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, "0") + + // THEN listeners are notified + foregroundExecutor.advanceClockToLast() + foregroundExecutor.runAllReady() + verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(true)) + } + + @Test fun testOnMediaDataChanged_updatesLastActiveTime() { val currentTime = clock.elapsedRealtime() mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTime) } @@ -402,7 +442,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.setTimedOut(KEY, true, true) // THEN the last active time is not changed - verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime) } @@ -413,7 +454,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) @@ -425,7 +467,8 @@ class MediaDataManagerTest : SysuiTestCase() { // THEN the last active time is not changed verify(listener) - .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime) } @@ -451,7 +494,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) // THEN only the first MAX_COMPACT_ACTIONS are actually set - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo( MediaDataManager.MAX_COMPACT_ACTIONS) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt index c6d7e92175eb..b9caab277c4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt @@ -185,7 +185,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { filter.onMediaDataLoaded(KEY, null, mediaData1) bgExecutor.runAllReady() fgExecutor.runAllReady() - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -207,7 +208,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -236,7 +238,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -251,14 +254,15 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(KEY, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is filtered verify(mediaListener, never()).onMediaDataLoaded( - eq(KEY), eq(null), eq(mediaData2), anyBoolean()) + eq(KEY), eq(null), eq(mediaData2), anyBoolean(), anyBoolean()) } @Test @@ -274,7 +278,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { fgExecutor.runAllReady() // THEN the event is not filtered because there isn't a notification for the remote // session. - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -291,14 +296,15 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true), + eq(false)) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(key2, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is filtered verify(mediaListener, never()) - .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean()) + .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean()) // AND there should be a removed event for key2 verify(mediaListener).onMediaDataRemoved(eq(key2)) } @@ -317,13 +323,15 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true), + eq(false)) // WHEN a loaded event is received that matches the remote session filter.onMediaDataLoaded(key2, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true), + eq(false)) } @Test @@ -339,13 +347,15 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(KEY, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true), + eq(false)) } @Test @@ -363,7 +373,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -385,7 +396,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the key migration event is fired - verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true), + eq(false)) } @Test @@ -415,12 +427,13 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { fgExecutor.runAllReady() // THEN the key migration event is filtered verify(mediaListener, never()) - .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean()) + .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean()) // WHEN a loaded event is received that matches the remote session filter.onMediaDataLoaded(key2, null, mediaData1) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the key migration event is fired - verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true), + eq(false)) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index 007a3b93eca8..33c7a571ce27 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -17,6 +17,7 @@ package com.android.systemui.people; import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; +import static com.android.systemui.people.PeopleSpaceUtils.getContactLookupKeysWithBirthdaysToday; import static com.google.common.truth.Truth.assertThat; @@ -35,6 +36,7 @@ import android.app.Person; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; +import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -64,12 +66,17 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; @@ -112,6 +119,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setNotificationDataUri(URI) .setMessagesCount(1) .build(); + private static final String TEST_DISPLAY_NAME = "Display Name"; private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).setLongLabel( @@ -227,6 +235,12 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3)); } + @After + public void tearDown() { + cleanupTestContactFromContactProvider(); + } + + @Test public void testAugmentTileFromNotification() { PeopleSpaceTile tile = @@ -467,4 +481,82 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { eq(WIDGET_ID_WITH_SHORTCUT), any()); } + + @Test + public void testBirthdayQueriesWithYear() throws Exception { + String birthdayToday = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + addBirthdayToContactsDatabase(birthdayToday); + + List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext); + + assertThat(lookupKeys).hasSize(1); + } + + @Test + public void testBirthdayQueriesWithoutYear() throws Exception { + String birthdayToday = new SimpleDateFormat("--MM-dd").format(new Date()); + addBirthdayToContactsDatabase(birthdayToday); + + List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext); + + assertThat(lookupKeys).hasSize(1); + } + + @Test + public void testBirthdayQueriesWithDifferentDates() throws Exception { + Date yesterday = new Date(System.currentTimeMillis() - Duration.ofDays(1).toMillis()); + String birthdayYesterday = new SimpleDateFormat("--MM-dd").format(yesterday); + addBirthdayToContactsDatabase(birthdayYesterday); + + List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext); + + assertThat(lookupKeys).isEmpty(); + } + + private void addBirthdayToContactsDatabase(String birthdayDate) throws Exception { + ContentResolver resolver = mContext.getContentResolver(); + ArrayList<ContentProviderOperation> ops = new ArrayList<>(3); + ops.add(ContentProviderOperation + .newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google") + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "fakeAccountName") + .build()); + ops.add(ContentProviderOperation + .newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, + TEST_DISPLAY_NAME) + .build()); + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference( + ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue( + ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE) + .withValue( + ContactsContract.CommonDataKinds.Event.TYPE, + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) + .withValue( + ContactsContract.CommonDataKinds.Event.START_DATE, birthdayDate) + .build()); + resolver.applyBatch(ContactsContract.AUTHORITY, ops); + } + + private void cleanupTestContactFromContactProvider() { + Cursor cursor = mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, + null, + ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + "=?", + new String[]{TEST_DISPLAY_NAME}, + null); + while (cursor.moveToNext()) { + String contactId = cursor.getString(cursor.getColumnIndex( + ContactsContract.Contacts.NAME_RAW_CONTACT_ID)); + mContext.getContentResolver().delete(ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.RAW_CONTACT_ID + "=?", + new String[]{contactId}); + } + cursor.close(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java index 5f4d90b3666f..f6264ffc6a70 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java @@ -49,6 +49,7 @@ import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubble; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -213,6 +214,7 @@ public class LaunchConversationActivityTest extends SysuiTestCase { verify(mBubblesManager, never()).expandStackAndSelectBubble(any(NotificationEntry.class)); } + @Ignore @Test public void testBubbleWithNoNotifOpensBubble() throws Exception { Bubble bubble = mock(Bubble.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 7af374308bef..c48f26b8c853 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -43,6 +43,7 @@ import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS; import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS; import static android.content.Intent.ACTION_BOOT_COMPLETED; import static android.content.Intent.ACTION_PACKAGES_SUSPENDED; +import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.PermissionChecker.PERMISSION_GRANTED; import static android.content.PermissionChecker.PERMISSION_HARD_DENIED; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; @@ -88,7 +89,6 @@ import android.content.pm.ShortcutInfo; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.service.notification.ConversationChannelWrapper; @@ -1104,7 +1104,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testGetPeopleTileFromPersistentStorageNoConversation() throws RemoteException { + public void testGetPeopleTileFromPersistentStorageNoConversation() throws Exception { when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(null); PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A); PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT); @@ -1223,8 +1223,38 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChange() { - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + public void testUpdateWidgetsFromBroadcastInBackgroundBootCompleteWithPackageUninstalled() + throws Exception { + when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow( + PackageManager.NameNotFoundException.class); + + // We should remove widgets if the package is uninstalled at next reboot if we missed the + // package removed broadcast. + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); + + PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); + assertThat(tile).isNull(); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateWidgetsFromBroadcastInBackgroundPackageRemovedWithPackageUninstalled() + throws Exception { + when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow( + PackageManager.NameNotFoundException.class); + + mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGE_REMOVED); + + PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); + assertThat(tile).isNull(); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateWidgetsFromBroadcastInBackground() { + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.isPackageSuspended()).isFalse(); @@ -1236,10 +1266,10 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeWithUserQuieted() { + public void testUpdateWidgetsFromBroadcastInBackgroundWithUserQuieted() { when(mUserManager.isQuietModeEnabled(any())).thenReturn(true); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.isPackageSuspended()).isFalse(); @@ -1248,10 +1278,10 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeWithPackageSuspended() throws Exception { + public void testUpdateWidgetsFromBroadcastInBackgroundWithPackageSuspended() throws Exception { when(mPackageManager.isPackageSuspended(any())).thenReturn(true); - mManager.updateWidgetsOnStateChange(ACTION_PACKAGES_SUSPENDED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGES_SUSPENDED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.isPackageSuspended()).isTrue(); @@ -1260,9 +1290,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeNotInDnd() { + public void testUpdateWidgetsFromBroadcastInBackgroundNotInDnd() { int expected = 0; - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1270,14 +1300,14 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllConversations() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllConversations() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_PRIORITY); when(mNotificationPolicy.allowConversations()).thenReturn(true); setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_ANYONE); - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1285,7 +1315,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllowOnlyImportantConversations() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowOnlyImportantConversations() { int expected = 0; // Only allow important conversations. when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( @@ -1293,7 +1323,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mNotificationPolicy.allowConversations()).thenReturn(true); setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_IMPORTANT); - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1302,13 +1332,13 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllowNoConversations() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversations() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_PRIORITY); when(mNotificationPolicy.allowConversations()).thenReturn(false); - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1316,7 +1346,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowContactMessages() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowContactMessages() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_PRIORITY); @@ -1324,14 +1354,14 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_CONTACT); when(mNotificationPolicy.allowMessages()).thenReturn(true); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONTACTS); } @Test - public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowStarredContactMessages() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowStarredContactMessages() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_PRIORITY); @@ -1339,26 +1369,26 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_STAR); when(mNotificationPolicy.allowMessages()).thenReturn(true); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_STARRED_CONTACTS); setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_AMBIENT); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS); } @Test - public void testUpdateWidgetsOnStateChangeAllowAlarmsOnly() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowAlarmsOnly() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_ALARMS); - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1366,7 +1396,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllowVisualEffectsAndAllowAlarmsOnly() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowVisualEffectsAndAllowAlarmsOnly() { int expected = 0; // If we show visuals, but just only make sounds for alarms, still show content in tiles. when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( @@ -1374,7 +1404,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_AMBIENT); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index b0e3e3e936a9..2ae4cbe17ac6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -46,6 +46,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.dagger.QSFragmentComponent; +import com.android.systemui.qs.external.CustomTileStatePersister; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.settings.UserTracker; @@ -132,7 +133,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { () -> mock(AutoTileManager.class), mock(DumpManager.class), mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)), mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class), - mock(SecureSettings.class)); + mock(SecureSettings.class), mock(CustomTileStatePersister.class)); qs.setHost(host); qs.setListening(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 7c73b4c44e90..69bdcbcff270 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -59,6 +59,8 @@ import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.external.CustomTile; +import com.android.systemui.qs.external.CustomTileStatePersister; +import com.android.systemui.qs.external.TileServiceKey; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserTracker; @@ -125,6 +127,8 @@ public class QSTileHostTest extends SysuiTestCase { private UserTracker mUserTracker; @Mock private SecureSettings mSecureSettings; + @Mock + private CustomTileStatePersister mCustomTileStatePersister; private Handler mHandler; private TestableLooper mLooper; @@ -145,7 +149,7 @@ public class QSTileHostTest extends SysuiTestCase { mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler, mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager, mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker, - mSecureSettings); + mSecureSettings, mCustomTileStatePersister); setUpTileFactory(); when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt())) @@ -371,6 +375,14 @@ public class QSTileHostTest extends SysuiTestCase { verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString()); } + @Test + public void testCustomTileRemoved_stateDeleted() { + mQSTileHost.changeTiles(List.of(CUSTOM_TILE_SPEC), List.of()); + + verify(mCustomTileStatePersister) + .removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId())); + } + private class TestQSTileHost extends QSTileHost { TestQSTileHost(Context context, StatusBarIconController iconController, QSFactory defaultFactory, Handler mainHandler, Looper bgLooper, @@ -378,10 +390,11 @@ public class QSTileHostTest extends SysuiTestCase { Provider<AutoTileManager> autoTiles, DumpManager dumpManager, BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger, UiEventLogger uiEventLogger, UserTracker userTracker, - SecureSettings secureSettings) { + SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) { super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager, tunerService, autoTiles, dumpManager, broadcastDispatcher, - Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings); + Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings, + customTileStatePersister); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt new file mode 100644 index 000000000000..6c96576bcbc1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.external + +import android.content.ComponentName +import android.content.Context +import android.content.SharedPreferences +import android.service.quicksettings.Tile +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.eq +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Answers +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class CustomTileStatePersisterTest : SysuiTestCase() { + + companion object { + private val TEST_COMPONENT = ComponentName("pkg", "cls") + private const val TEST_USER = 0 + private val KEY = TileServiceKey(TEST_COMPONENT, TEST_USER) + + private const val TEST_STATE = Tile.STATE_INACTIVE + private const val TEST_LABEL = "test_label" + private const val TEST_SUBTITLE = "test_subtitle" + private const val TEST_CONTENT_DESCRIPTION = "test_content_description" + private const val TEST_STATE_DESCRIPTION = "test_state_description" + + private fun Tile.isEqualTo(other: Tile): Boolean { + return state == other.state && + label == other.label && + subtitle == other.subtitle && + contentDescription == other.contentDescription && + stateDescription == other.stateDescription + } + } + + @Mock + private lateinit var mockContext: Context + @Mock + private lateinit var sharedPreferences: SharedPreferences + @Mock(answer = Answers.RETURNS_SELF) + private lateinit var editor: SharedPreferences.Editor + private lateinit var tile: Tile + private lateinit var customTileStatePersister: CustomTileStatePersister + + @Captor + private lateinit var stringCaptor: ArgumentCaptor<String> + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + `when`(mockContext.getSharedPreferences(anyString(), anyInt())) + .thenReturn(sharedPreferences) + `when`(sharedPreferences.edit()).thenReturn(editor) + + tile = Tile() + customTileStatePersister = CustomTileStatePersister(mockContext) + } + + @Test + fun testWriteState() { + tile.apply { + state = TEST_STATE + label = TEST_LABEL + subtitle = TEST_SUBTITLE + contentDescription = TEST_CONTENT_DESCRIPTION + stateDescription = TEST_STATE_DESCRIPTION + } + + customTileStatePersister.persistState(KEY, tile) + + verify(editor).putString(eq(KEY.toString()), capture(stringCaptor)) + + assertThat(tile.isEqualTo(readTileFromString(stringCaptor.value))).isTrue() + } + + @Test + fun testReadState() { + tile.apply { + state = TEST_STATE + label = TEST_LABEL + subtitle = TEST_SUBTITLE + contentDescription = TEST_CONTENT_DESCRIPTION + stateDescription = TEST_STATE_DESCRIPTION + } + + `when`(sharedPreferences.getString(eq(KEY.toString()), any())) + .thenReturn(writeToString(tile)) + + assertThat(tile.isEqualTo(customTileStatePersister.readState(KEY)!!)).isTrue() + } + + @Test + fun testReadStateDefault() { + `when`(sharedPreferences.getString(any(), any())).thenAnswer { + it.getArgument(1) + } + + assertThat(customTileStatePersister.readState(KEY)).isNull() + } + + @Test + fun testStoreNulls() { + assertThat(tile.label).isNull() + + customTileStatePersister.persistState(KEY, tile) + + verify(editor).putString(eq(KEY.toString()), capture(stringCaptor)) + + assertThat(readTileFromString(stringCaptor.value).label).isNull() + } + + @Test + fun testReadNulls() { + assertThat(tile.label).isNull() + + `when`(sharedPreferences.getString(eq(KEY.toString()), any())) + .thenReturn(writeToString(tile)) + + assertThat(customTileStatePersister.readState(KEY)!!.label).isNull() + } + + @Test + fun testRemoveState() { + customTileStatePersister.removeState(KEY) + + verify(editor).remove(KEY.toString()) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt index b1c3d1da8fea..9b5c1619ef31 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.util.mockito.any import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -48,8 +49,9 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.`when` -import org.mockito.Mockito.any import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @@ -76,6 +78,7 @@ class CustomTileTest : SysuiTestCase() { @Mock private lateinit var packageManager: PackageManager @Mock private lateinit var applicationInfo: ApplicationInfo @Mock private lateinit var serviceInfo: ServiceInfo + @Mock private lateinit var customTileStatePersister: CustomTileStatePersister private lateinit var customTile: CustomTile private lateinit var testableLooper: TestableLooper @@ -108,10 +111,13 @@ class CustomTileTest : SysuiTestCase() { metricsLogger, statusBarStateController, activityStarter, - qsLogger + qsLogger, + customTileStatePersister ) customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext) + customTile.initialize() + testableLooper.processAllMessages() } @Test @@ -123,6 +129,8 @@ class CustomTileTest : SysuiTestCase() { `when`(userContext.userId).thenReturn(10) val tile = CustomTile.create(customTileBuilder, TILE_SPEC, userContext) + tile.initialize() + testableLooper.processAllMessages() assertEquals(10, tile.user) } @@ -131,6 +139,8 @@ class CustomTileTest : SysuiTestCase() { fun testToggleableTileHasBooleanState() { `when`(tileServiceManager.isToggleableTile).thenReturn(true) customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext) + customTile.initialize() + testableLooper.processAllMessages() assertTrue(customTile.state is QSTile.BooleanState) assertTrue(customTile.newTileState() is QSTile.BooleanState) @@ -146,6 +156,9 @@ class CustomTileTest : SysuiTestCase() { fun testValueUpdatedInBooleanTile() { `when`(tileServiceManager.isToggleableTile).thenReturn(true) customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext) + customTile.initialize() + testableLooper.processAllMessages() + customTile.qsTile.icon = mock(Icon::class.java) `when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java))) .thenReturn(mock(Drawable::class.java)) @@ -173,4 +186,88 @@ class CustomTileTest : SysuiTestCase() { .thenReturn(null) customTile.handleUpdateState(customTile.newTileState(), null) } + + @Test + fun testNoLoadStateTileNotActive() { + // Not active by default + testableLooper.processAllMessages() + + verify(customTileStatePersister, never()).readState(any()) + } + + @Test + fun testNoPersistedStateTileNotActive() { + // Not active by default + val t = Tile().apply { + state = Tile.STATE_INACTIVE + } + customTile.updateTileState(t) + testableLooper.processAllMessages() + + verify(customTileStatePersister, never()).persistState(any(), any()) + } + + @Test + fun testPersistedStateRetrieved() { + val state = Tile.STATE_INACTIVE + val label = "test_label" + val subtitle = "test_subtitle" + val contentDescription = "test_content_description" + val stateDescription = "test_state_description" + + val t = Tile().apply { + this.state = state + this.label = label + this.subtitle = subtitle + this.contentDescription = contentDescription + this.stateDescription = stateDescription + } + `when`(tileServiceManager.isActiveTile).thenReturn(true) + `when`(customTileStatePersister + .readState(TileServiceKey(componentName, customTile.user))).thenReturn(t) + val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext) + tile.initialize() + testableLooper.processAllMessages() + + // Make sure we have an icon in the tile because we don't have a default icon + // This should not be overridden by the retrieved tile that has null icon. + tile.qsTile.icon = mock(Icon::class.java) + `when`(tile.qsTile.icon.loadDrawable(any(Context::class.java))) + .thenReturn(mock(Drawable::class.java)) + + tile.refreshState() + + testableLooper.processAllMessages() + + val tileState = tile.state + + assertEquals(state, tileState.state) + assertEquals(label, tileState.label) + assertEquals(subtitle, tileState.secondaryLabel) + assertEquals(contentDescription, tileState.contentDescription) + assertEquals(stateDescription, tileState.stateDescription) + } + + @Test + fun testStoreStateOnChange() { + val t = Tile().apply { + state = Tile.STATE_INACTIVE + label = "test_label" + subtitle = "test_subtitle" + contentDescription = "test_content_description" + stateDescription = "test_state_description" + } + `when`(tileServiceManager.isActiveTile).thenReturn(true) + + val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext) + tile.initialize() + testableLooper.processAllMessages() + + tile.updateTileState(t) + + testableLooper.processAllMessages() + + verify(customTileStatePersister) + .persistState(TileServiceKey(componentName, customTile.user), t) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java index 641f917bcfbe..2b1840462291 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java @@ -118,7 +118,8 @@ public class TileServicesTest extends SysuiTestCase { mQSLogger, mUiEventLogger, mUserTracker, - mSecureSettings); + mSecureSettings, + mock(CustomTileStatePersister.class)); mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher, mUserTracker); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index 80231a49bb44..ea4d7cc2529c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -121,6 +121,9 @@ public class QSTileImplTest extends SysuiTestCase { mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler, mFalsingManager, mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger); + mTile.initialize(); + mTestableLooper.processAllMessages(); + mTile.setTileSpec(SPEC); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt index 19ffa4991eaf..b8d018f5f850 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.qs.tileimpl import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest +import androidx.test.filters.MediumTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -27,7 +27,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) -@SmallTest +@MediumTest class TilesStatesTextTest : SysuiTestCase() { @Test @@ -51,4 +51,29 @@ class TilesStatesTextTest : SysuiTestCase() { assertThat(array.size).isEqualTo(3) } + + @Test + fun testStockTilesSubtitlesMap() { + val tiles = mContext.getString(R.string.quick_settings_tiles_stock).split(",") + tiles.forEach { spec -> + val resName = "${QSTileViewImpl.TILE_STATE_RES_PREFIX}$spec" + val resId = mContext.resources.getIdentifier(resName, "array", mContext.packageName) + + assertNotEquals("Missing resource for $resName", 0, resId) + + assertThat(SubtitleArrayMapping.getSubtitleId(spec)).isEqualTo(resId) + } + } + + @Test + fun testStockTilesSubtitlesReturnsDefault_unknown() { + assertThat(SubtitleArrayMapping.getSubtitleId("unknown")) + .isEqualTo(R.array.tile_states_default) + } + + @Test + fun testStockTilesSubtitlesReturnsDefault_null() { + assertThat(SubtitleArrayMapping.getSubtitleId(null)) + .isEqualTo(R.array.tile_states_default) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt index 32b1f433dfcf..5e2d8fde84da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt @@ -80,6 +80,8 @@ class AlarmTileTest : SysuiTestCase() { nextAlarmController ) + tile.initialize() + verify(nextAlarmController).observe(eq(tile), capture(callbackCaptor)) tile.refreshState() testableLooper.processAllMessages() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt index f17bd56d0052..1bf83513d472 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt @@ -87,6 +87,9 @@ class BatterySaverTileTest : SysuiTestCase() { qsLogger, batteryController, secureSettings) + + tile.initialize() + testableLooper.processAllMessages() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java index 7c1a5f5ebf30..d44a52607707 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -112,11 +112,14 @@ public class CastTileTest extends SysuiTestCase { mNetworkController, mHotspotController ); + mCastTile.initialize(); // We are not setting the mocks to listening, so we trigger a first refresh state to // set the initial state mCastTile.refreshState(); + mTestableLooper.processAllMessages(); + mCastTile.handleSetListening(true); ArgumentCaptor<NetworkController.SignalCallback> signalCallbackArgumentCaptor = ArgumentCaptor.forClass(NetworkController.SignalCallback.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt index 6d1bbd9708ea..94af10a485fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt @@ -378,7 +378,10 @@ class DeviceControlsTileTest : SysuiTestCase() { qsLogger, controlsComponent, keyguardStateController - ) + ).also { + it.initialize() + testableLooper.processAllMessages() + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java index 99d028cd8c5c..cfd37358dcff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java @@ -92,6 +92,9 @@ public class NfcTileTest extends SysuiTestCase { mQSLogger, mBroadcastDispatcher ); + + mNfcTile.initialize(); + mTestableLooper.processAllMessages(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index e4a9aacb57ab..a50cbe5adc48 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -155,6 +155,9 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mPackageManager, mSecureSettings, mController); + + mTile.initialize(); + mTestableLooper.processAllMessages(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java index df4908ddc4ef..9eb688de3511 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java @@ -89,6 +89,9 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase { mActivityStarter, mQSLogger ); + + mTile.initialize(); + mTestableLooper.processAllMessages(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index 3b4e863ed8bd..964ce01312bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -91,6 +91,9 @@ public class ScreenRecordTileTest extends SysuiTestCase { mController, mKeyguardDismissUtil ); + + mTile.initialize(); + mTestableLooper.processAllMessages(); } // Test that the tile is inactive and labeled correctly when the controller is neither starting diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 2e0827f24bf8..fa25c3f1e005 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -289,6 +289,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { @Test public void testIconScrollXAfterTranslationAndReset() throws Exception { + mGroupRow.setDismissUsingRowTranslationX(false); mGroupRow.setTranslation(50); assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 43f7284e477e..04d7b7261ba7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.stack; import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED; +import static android.view.View.GONE; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; @@ -316,6 +317,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { public void testUpdateFooter_oneClearableNotification() { setBarStateForTest(StatusBarState.SHADE); + when(mEmptyShadeView.getVisibility()).thenReturn(GONE); when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL)) .thenReturn(true); when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true); @@ -337,6 +339,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true); when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL)) .thenReturn(false); + when(mEmptyShadeView.getVisibility()).thenReturn(GONE); FooterView view = mock(FooterView.class); mStackScroller.setFooterView(view); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java index 9ac600afe990..5bf1bb3c573f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java @@ -63,6 +63,7 @@ public class DozeParametersTest extends SysuiTestCase { @Mock private BatteryController mBatteryController; @Mock private FeatureFlags mFeatureFlags; @Mock private DumpManager mDumpManager; + @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Before public void setup() { @@ -75,7 +76,8 @@ public class DozeParametersTest extends SysuiTestCase { mBatteryController, mTunerService, mDumpManager, - mFeatureFlags + mFeatureFlags, + mUnlockedScreenOffAnimationController ); } @Test @@ -125,7 +127,8 @@ public class DozeParametersTest extends SysuiTestCase { when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true); mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1"); when(mFeatureFlags.useNewLockscreenAnimations()).thenReturn(true); - + when(mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation()) + .thenReturn(true); assertTrue(mDozeParameters.shouldControlUnlockedScreenOff()); // Trigger the setter for the current value. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 9e939eefa705..2d51683c8ae5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -84,7 +84,6 @@ import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.QSDetailDisplayer; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FeatureFlags; @@ -675,21 +674,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { verify(mTapAgainViewController).show(); } - @Test - public void testNotificationClipping_isAlignedWithNotificationScrimInSplitShade() { - mStatusBarStateController.setState(SHADE); - QS qs = mock(QS.class); - when(qs.getHeader()).thenReturn(mock(View.class)); - mNotificationPanelViewController.mQs = qs; - enableSplitShade(); - - // hacky way to refresh notification scrim top with non-zero qsPanelBottom value - mNotificationPanelViewController.setTransitionToFullShadeAmount(200, false, 0); - - verify(mAmbientState) - .setNotificationScrimTop(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE); - } - private FalsingManager.FalsingTapListener getFalsingTapListener() { for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { listener.onViewAttachedToWindow(mView); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java index 323843098a1a..9fe47eceff1b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java @@ -45,6 +45,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Test; @@ -70,6 +71,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Mock private SysuiColorExtractor mColorExtractor; @Mock ColorExtractor.GradientColors mGradientColors; @Mock private DumpManager mDumpManager; + @Mock private KeyguardStateController mKeyguardStateController; @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters; private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; @@ -83,7 +85,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, - mColorExtractor, mDumpManager); + mColorExtractor, mDumpManager, mKeyguardStateController); mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); mNotificationShadeWindowController.attach(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index e55361e750b1..678b193073c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -312,6 +312,8 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimBehind, true, mScrimForBubble, false )); + + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); } @Test @@ -321,8 +323,9 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE, + mScrimBehind, TRANSPARENT, mNotificationsScrim, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); assertScrimTinted(Map.of( mScrimInFront, true, @@ -340,6 +343,7 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, mScrimBehind, TRANSPARENT)); + assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // Pulsing notification should conserve AOD wallpaper. mScrimController.transitionTo(ScrimState.PULSING); @@ -348,6 +352,7 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, mScrimBehind, TRANSPARENT)); + assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); } @Test @@ -359,7 +364,8 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); assertScrimTinted(Map.of( mScrimInFront, true, @@ -378,7 +384,8 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); assertScrimTinted(Map.of( mScrimInFront, true, @@ -403,13 +410,15 @@ public class ScrimControllerTest extends SysuiTestCase { finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, SEMI_TRANSPARENT, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... and that if we set it while we're in AOD, it does take immediate effect. mScrimController.setAodFrontScrimAlpha(1f); assertScrimAlpha(Map.of( mScrimInFront, OPAQUE, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... and make sure we recall the previous front scrim alpha even if we transition away // for a bit. @@ -418,7 +427,8 @@ public class ScrimControllerTest extends SysuiTestCase { finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, OPAQUE, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... and alpha updates should be completely ignored if always_on is off. // Passing it forward would mess up the wake-up transition. @@ -448,23 +458,28 @@ public class ScrimControllerTest extends SysuiTestCase { finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, OPAQUE, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... but will take effect after docked when(mDockManager.isDocked()).thenReturn(true); mScrimController.transitionTo(ScrimState.KEYGUARD); mScrimController.setAodFrontScrimAlpha(0.5f); mScrimController.transitionTo(ScrimState.AOD); + finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, SEMI_TRANSPARENT, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... and that if we set it while we're in AOD, it does take immediate effect after docked. mScrimController.setAodFrontScrimAlpha(1f); + finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, OPAQUE, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // Reset value since enums are static. mScrimController.setAodFrontScrimAlpha(0f); @@ -480,7 +495,8 @@ public class ScrimControllerTest extends SysuiTestCase { finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); mScrimController.transitionTo(ScrimState.PULSING); finishAnimationsImmediately(); @@ -489,7 +505,8 @@ public class ScrimControllerTest extends SysuiTestCase { // Pulse callback should have been invoked assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); assertScrimTinted(Map.of( mScrimInFront, true, @@ -503,13 +520,16 @@ public class ScrimControllerTest extends SysuiTestCase { // Front scrim should be semi-transparent assertScrimAlpha(Map.of( mScrimInFront, SEMI_TRANSPARENT, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); mScrimController.setWakeLockScreenSensorActive(true); finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, SEMI_TRANSPARENT, - mScrimBehind, SEMI_TRANSPARENT)); + mScrimBehind, TRANSPARENT)); + assertEquals(ScrimController.WAKE_SENSOR_SCRIM_ALPHA, + mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // Reset value since enums are static. mScrimController.setAodFrontScrimAlpha(0f); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index b2efd682bc62..cbc7c6dd0447 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -777,6 +777,12 @@ public class StatusBarTest extends SysuiTestCase { } @Test + public void testDumpBarTransitions_DoesNotCrash() { + StatusBar.dumpBarTransitions( + new PrintWriter(new ByteArrayOutputStream()), "var", /* transitions= */ null); + } + + @Test @RunWithLooper(setAsMainLooper = true) public void testUpdateKeyguardState_DoesNotCrash() { mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); @@ -843,12 +849,14 @@ public class StatusBarTest extends SysuiTestCase { // By default, showKeyguardImpl sets state to KEYGUARD. mStatusBar.showKeyguardImpl(); - verify(mStatusBarStateController).setState(eq(StatusBarState.KEYGUARD)); + verify(mStatusBarStateController).setState( + eq(StatusBarState.KEYGUARD), eq(false) /* force */); // If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER. when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true); mStatusBar.showKeyguardImpl(); - verify(mStatusBarStateController).setState(eq(StatusBarState.FULLSCREEN_USER_SWITCHER)); + verify(mStatusBarStateController).setState( + eq(StatusBarState.FULLSCREEN_USER_SWITCHER), eq(false) /* force */); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java index 1aebf1c1c80d..e136d00b86f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java @@ -132,4 +132,9 @@ public class FakeKeyguardStateController implements KeyguardStateController { public boolean canPerformSmartSpaceTransition() { return false; } + + @Override + public boolean isKeyguardScreenRotationAllowed() { + return false; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java index e6c740b3a263..30180897f5d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java @@ -67,7 +67,6 @@ import java.util.Collections; @SmallTest public class WalletScreenControllerTest extends SysuiTestCase { - private static final int MAX_CARDS = 10; private static final int CARD_CAROUSEL_WIDTH = 10; private static final String CARD_ID_1 = "card_id_1"; private static final String CARD_ID_2 = "card_id_2"; @@ -158,7 +157,7 @@ public class WalletScreenControllerTest extends SysuiTestCase { when(mKeyguardStateController.isUnlocked()).thenReturn(false); GetWalletCardsResponse response = new GetWalletCardsResponse( - Collections.singletonList(createWalletCard(mContext)), 0); + Collections.singletonList(createLockedWalletCard(mContext)), 0); mController.queryWalletCards(); mTestableLooper.processAllMessages(); @@ -406,6 +405,15 @@ public class WalletScreenControllerTest extends SysuiTestCase { .build(); } + private WalletCard createLockedWalletCard(Context context) { + PendingIntent pendingIntent = + PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); + return new WalletCard.Builder(CARD_ID_2, createIcon(), "•••• 5679", pendingIntent) + .setCardIcon(createIcon()) + .setCardLabel("Locked\nUnlock to pay") + .build(); + } + private WalletCard createWalletCard(Context context) { PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); 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 6e2e4cb9ecfa..9fa35f8bd9f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -23,6 +23,8 @@ import static android.service.notification.NotificationListenerService.REASON_CA import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -37,6 +39,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -98,6 +101,7 @@ import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; @@ -227,6 +231,8 @@ public class BubblesTest extends SysuiTestCase { private TaskStackListenerImpl mTaskStackListener; @Mock private ShellTaskOrganizer mShellTaskOrganizer; + @Mock + private KeyguardStateController mKeyguardStateController; private TestableBubblePositioner mPositioner; @@ -249,7 +255,7 @@ public class BubblesTest extends SysuiTestCase { mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, - mColorExtractor, mDumpManager); + mColorExtractor, mDumpManager, mKeyguardStateController); mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); mNotificationShadeWindowController.attach(); @@ -287,6 +293,7 @@ public class BubblesTest extends SysuiTestCase { // TODO: Fix mPositioner = new TestableBubblePositioner(mContext, mWindowManager); + mPositioner.setMaxBubbles(5); mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor); TestableNotificationInterruptStateProviderImpl interruptionStateProvider = @@ -320,6 +327,7 @@ public class BubblesTest extends SysuiTestCase { syncExecutor, mock(Handler.class)); mBubbleController.setExpandListener(mBubbleExpandListener); + spyOn(mBubbleController); mBubblesManager = new BubblesManager( mContext, @@ -466,7 +474,7 @@ public class BubblesTest extends SysuiTestCase { @Test public void testExpandCollapseStack() { - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow); @@ -474,25 +482,23 @@ public class BubblesTest extends SysuiTestCase { // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); assertTrue(mSysUiStateBubblesExpanded); // Make sure the notif is suppressed - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Collapse mBubbleController.collapseStack(); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); assertFalse(mSysUiStateBubblesExpanded); } @@ -508,15 +514,13 @@ public class BubblesTest extends SysuiTestCase { // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry2); // Expand BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( true, mRow2.getKey()); @@ -524,8 +528,7 @@ public class BubblesTest extends SysuiTestCase { // Last added is the one that is expanded assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey()); - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry2); // Switch which bubble is expanded mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey( @@ -533,8 +536,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleData.setExpanded(true); assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // collapse for previous bubble verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( @@ -545,7 +547,7 @@ public class BubblesTest extends SysuiTestCase { // Collapse mBubbleController.collapseStack(); - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); assertFalse(mSysUiStateBubblesExpanded); } @@ -558,22 +560,20 @@ public class BubblesTest extends SysuiTestCase { // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); // Expand mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); assertTrue(mSysUiStateBubblesExpanded); // Notif is suppressed after expansion - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); } @@ -586,22 +586,20 @@ public class BubblesTest extends SysuiTestCase { // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); // Expand mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); assertTrue(mSysUiStateBubblesExpanded); // Notif is suppressed after expansion - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); @@ -610,8 +608,7 @@ public class BubblesTest extends SysuiTestCase { // Nothing should have changed // Notif is suppressed after expansion - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); } @@ -630,14 +627,13 @@ public class BubblesTest extends SysuiTestCase { assertTrue(mSysUiStateBubblesExpanded); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey()); // Last added is the one that is expanded assertEquals(mRow2.getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry2); // Dismiss currently expanded mBubbleController.removeBubble( @@ -675,7 +671,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleData.setExpanded(true); assertTrue(mSysUiStateBubblesExpanded); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); // Block the bubble so it won't be in the overflow @@ -694,7 +690,7 @@ public class BubblesTest extends SysuiTestCase { @Test public void testAutoExpand_fails_noFlag() { - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); setMetadataFlags(mRow, Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */); @@ -705,7 +701,7 @@ public class BubblesTest extends SysuiTestCase { // Expansion shouldn't change verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, mRow.getKey()); - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); assertFalse(mSysUiStateBubblesExpanded); } @@ -722,7 +718,7 @@ public class BubblesTest extends SysuiTestCase { // Expansion should change verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, mRow.getKey()); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); assertTrue(mSysUiStateBubblesExpanded); } @@ -737,8 +733,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed because we were foreground - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); @@ -751,8 +746,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); // Should not be suppressed - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Should show dot assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); @@ -762,8 +756,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); @@ -788,8 +781,7 @@ public class BubblesTest extends SysuiTestCase { @Test public void testMarkNewNotificationAsShowInShade() { mEntryListener.onPendingEntryAdded(mRow); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); @@ -874,8 +866,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( mRow.getKey(), mRow, REASON_CANCEL_ALL); @@ -883,8 +874,7 @@ public class BubblesTest extends SysuiTestCase { // Intercept! assertTrue(intercepted); // Should update show in shade state - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); } @Test @@ -893,8 +883,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( mRow.getKey(), mRow, REASON_CANCEL); @@ -902,8 +891,7 @@ public class BubblesTest extends SysuiTestCase { // Intercept! assertTrue(intercepted); // Should update show in shade state - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); } @Test @@ -912,8 +900,7 @@ public class BubblesTest extends SysuiTestCase { mEntryListener.onPendingEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Dismiss the bubble into overflow. mBubbleController.removeBubble( @@ -934,8 +921,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mBubbleController.removeBubble( mRow.getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE); @@ -981,48 +967,36 @@ public class BubblesTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_notificationDismiss() { - Bubbles.SuppressionChangedListener listener = - mock(Bubbles.SuppressionChangedListener.class); - mBubbleData.setSuppressionChangedListener(listener); - mEntryListener.onPendingEntryAdded(mRow); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mRemoveInterceptor.onNotificationRemoveRequested( mRow.getKey(), mRow, REASON_CANCEL); // Should update show in shade state - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Should notify delegate that shade state changed - verify(listener).onBubbleNotificationSuppressionChange( + verify(mBubbleController).onBubbleNotificationSuppressionChanged( mBubbleData.getBubbleInStackWithKey(mRow.getKey())); } @Test public void testNotifyShadeSuppressionChange_bubbleExpanded() { - Bubbles.SuppressionChangedListener listener = - mock(Bubbles.SuppressionChangedListener.class); - mBubbleData.setSuppressionChangedListener(listener); - mEntryListener.onPendingEntryAdded(mRow); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mBubbleData.setExpanded(true); // Once a bubble is expanded the notif is suppressed - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Should notify delegate that shade state changed - verify(listener).onBubbleNotificationSuppressionChange( + verify(mBubbleController).onBubbleNotificationSuppressionChanged( mBubbleData.getBubbleInStackWithKey(mRow.getKey())); } @@ -1042,7 +1016,11 @@ public class BubblesTest extends SysuiTestCase { // THEN the summary and bubbled child are suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry().getKey(), groupSummary.getEntry().getSbn().getGroupKey())); + groupedBubble.getEntry().getKey(), + groupSummary.getEntry().getSbn().getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + groupedBubble.getEntry().getKey(), + groupSummary.getEntry().getSbn().getGroupKey())); assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey())); } @@ -1098,6 +1076,9 @@ public class BubblesTest extends SysuiTestCase { assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( groupedBubble.getEntry().getKey(), groupedBubble.getEntry().getSbn().getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + groupedBubble.getEntry().getKey(), + groupedBubble.getEntry().getSbn().getGroupKey())); // THEN the summary is removed from GroupManager verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry()); @@ -1253,4 +1234,42 @@ public class BubblesTest extends SysuiTestCase { Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble)) .build(); } + + /** + * Asserts that the bubble stack is expanded and also validates the cached state is updated. + */ + private void assertStackExpanded() { + assertTrue(mBubbleController.isStackExpanded()); + assertTrue(mBubbleController.getImplCachedState().isStackExpanded()); + } + + /** + * Asserts that the bubble stack is collapsed and also validates the cached state is updated. + */ + private void assertStackCollapsed() { + assertFalse(mBubbleController.isStackExpanded()); + assertFalse(mBubbleController.getImplCachedState().isStackExpanded()); + } + + /** + * Asserts that a bubble notification is suppressed from the shade and also validates the cached + * state is updated. + */ + private void assertBubbleNotificationSuppressedFromShade(BubbleEntry entry) { + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getGroupKey())); + } + + /** + * Asserts that a bubble notification is not suppressed from the shade and also validates the + * cached state is updated. + */ + private void assertBubbleNotificationNotSuppressedFromShade(BubbleEntry entry) { + assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getGroupKey())); + assertFalse(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getGroupKey())); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index 9339f81940d9..55cc8bb1780d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -19,6 +19,8 @@ package com.android.systemui.wmshell; import static android.app.Notification.FLAG_BUBBLE; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -82,6 +84,7 @@ import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; @@ -192,6 +195,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { private TaskStackListenerImpl mTaskStackListener; @Mock private ShellTaskOrganizer mShellTaskOrganizer; + @Mock + private KeyguardStateController mKeyguardStateController; private TestableBubblePositioner mPositioner; @@ -213,7 +218,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, - mColorExtractor, mDumpManager); + mColorExtractor, mDumpManager, mKeyguardStateController); mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); mNotificationShadeWindowController.attach(); @@ -232,6 +237,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); mPositioner = new TestableBubblePositioner(mContext, mWindowManager); + mPositioner.setMaxBubbles(5); mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor); TestableNotificationInterruptStateProviderImpl interruptionStateProvider = @@ -264,6 +270,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { syncExecutor, mock(Handler.class)); mBubbleController.setExpandListener(mBubbleExpandListener); + spyOn(mBubbleController); mBubblesManager = new BubblesManager( mContext, @@ -324,8 +331,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Make it look like dismissed notif mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true); @@ -348,8 +354,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { .thenReturn(mRow); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Make it look like dismissed notif mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true); @@ -384,7 +389,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { @Test public void testExpandCollapseStack() { - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow); @@ -392,23 +397,20 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Expand the stack - BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); // Make sure the notif is suppressed - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Collapse mBubbleController.collapseStack(); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); } @Test @@ -422,22 +424,19 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry2); // Expand BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( true, mRow2.getKey()); // Last added is the one that is expanded assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey()); - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry2); // Switch which bubble is expanded mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey( @@ -445,8 +444,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubbleData.setExpanded(true); assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // collapse for previous bubble verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( @@ -458,7 +456,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { // Collapse mBubbleController.collapseStack(); - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); } @Test @@ -469,20 +467,18 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); // Expand mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); // Notif is suppressed after expansion - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); } @@ -495,20 +491,18 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); // Expand mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); // Notif is suppressed after expansion - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); @@ -517,8 +511,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { // Nothing should have changed // Notif is suppressed after expansion - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); } @@ -535,14 +528,13 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey()); // Last added is the one that is expanded assertEquals(mRow2.getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry2); // Dismiss currently expanded mBubbleController.removeBubble( @@ -578,7 +570,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); // Block the bubble so it won't be in the overflow @@ -597,7 +589,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { @Test public void testAutoExpand_fails_noFlag() { - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); setMetadataFlags(mRow, Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */); @@ -608,7 +600,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { // Expansion shouldn't change verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, mRow.getKey()); - assertFalse(mBubbleController.isStackExpanded()); + assertStackCollapsed(); } @Test @@ -623,7 +615,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { // Expansion should change verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, mRow.getKey()); - assertTrue(mBubbleController.isStackExpanded()); + assertStackExpanded(); } @Test @@ -636,8 +628,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed because we were foreground - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); @@ -648,8 +639,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); // Should not be suppressed - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Should show dot assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); @@ -659,8 +649,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); @@ -669,8 +658,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { @Test public void testMarkNewNotificationAsShowInShade() { mEntryListener.onEntryAdded(mRow); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); @@ -741,16 +729,14 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); boolean intercepted = mBubblesManager.handleDismissalInterception(mRow); // Intercept! assertTrue(intercepted); // Should update show in shade state - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); } @Test @@ -759,8 +745,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Dismiss the bubble mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); @@ -779,8 +764,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); // Dismiss the bubble mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); @@ -795,47 +779,35 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_notificationDismiss() { - Bubbles.SuppressionChangedListener listener = - mock(Bubbles.SuppressionChangedListener.class); - mBubbleData.setSuppressionChangedListener(listener); - mEntryListener.onEntryAdded(mRow); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mBubblesManager.handleDismissalInterception(mRow); // Should update show in shade state - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Should notify delegate that shade state changed - verify(listener).onBubbleNotificationSuppressionChange( + verify(mBubbleController).onBubbleNotificationSuppressionChanged( mBubbleData.getBubbleInStackWithKey(mRow.getKey())); } @Test public void testNotifyShadeSuppressionChange_bubbleExpanded() { - Bubbles.SuppressionChangedListener listener = - mock(Bubbles.SuppressionChangedListener.class); - mBubbleData.setSuppressionChangedListener(listener); - mEntryListener.onEntryAdded(mRow); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); mBubbleData.setExpanded(true); // Once a bubble is expanded the notif is suppressed - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); + assertBubbleNotificationSuppressedFromShade(mBubbleEntry); // Should notify delegate that shade state changed - verify(listener).onBubbleNotificationSuppressionChange( + verify(mBubbleController).onBubbleNotificationSuppressionChanged( mBubbleData.getBubbleInStackWithKey(mRow.getKey())); } @@ -857,6 +829,9 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( groupedBubble.getEntry().getKey(), groupedBubble.getEntry().getSbn().getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + groupedBubble.getEntry().getKey(), + groupedBubble.getEntry().getSbn().getGroupKey())); assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey())); } @@ -911,11 +886,17 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( groupedBubble.getEntry().getKey(), groupedBubble.getEntry().getSbn().getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + groupedBubble.getEntry().getKey(), + groupedBubble.getEntry().getSbn().getGroupKey())); // THEN the summary is also suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( groupSummary.getEntry().getKey(), groupSummary.getEntry().getSbn().getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + groupSummary.getEntry().getKey(), + groupSummary.getEntry().getSbn().getGroupKey())); } /** @@ -934,4 +915,42 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { } bubbleMetadata.setFlags(flags); } + + /** + * Asserts that the bubble stack is expanded and also validates the cached state is updated. + */ + private void assertStackExpanded() { + assertTrue(mBubbleController.isStackExpanded()); + assertTrue(mBubbleController.getImplCachedState().isStackExpanded()); + } + + /** + * Asserts that the bubble stack is collapsed and also validates the cached state is updated. + */ + private void assertStackCollapsed() { + assertFalse(mBubbleController.isStackExpanded()); + assertFalse(mBubbleController.getImplCachedState().isStackExpanded()); + } + + /** + * Asserts that a bubble notification is suppressed from the shade and also validates the cached + * state is updated. + */ + private void assertBubbleNotificationSuppressedFromShade(BubbleEntry entry) { + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getGroupKey())); + } + + /** + * Asserts that a bubble notification is not suppressed from the shade and also validates the + * cached state is updated. + */ + private void assertBubbleNotificationNotSuppressedFromShade(BubbleEntry entry) { + assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getGroupKey())); + assertFalse(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getGroupKey())); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java index 24a7cd5c89ac..6edc373d2926 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java @@ -22,9 +22,11 @@ import android.graphics.Insets; import android.graphics.Rect; import android.view.WindowManager; +import com.android.wm.shell.R; import com.android.wm.shell.bubbles.BubblePositioner; public class TestableBubblePositioner extends BubblePositioner { + private int mMaxBubbles; public TestableBubblePositioner(Context context, WindowManager windowManager) { @@ -33,5 +35,15 @@ public class TestableBubblePositioner extends BubblePositioner { updateInternal(Configuration.ORIENTATION_PORTRAIT, Insets.of(0, 0, 0, 0), new Rect(0, 0, 500, 1000)); + mMaxBubbles = context.getResources().getInteger(R.integer.bubbles_max_rendered); + } + + public void setMaxBubbles(int max) { + mMaxBubbles = max; + } + + @Override + public int getMaxBubbles() { + return mMaxBubbles; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 1dd0b21bda30..5691660b4882 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -27,6 +27,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.CommandQueue; @@ -37,6 +38,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; +import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.Pip; @@ -69,6 +71,7 @@ public class WMShellTest extends SysuiTestCase { @Mock LegacySplitScreen mLegacySplitScreen; @Mock OneHanded mOneHanded; @Mock HideDisplayCutout mHideDisplayCutout; + @Mock WakefulnessLifecycle mWakefulnessLifecycle; @Mock ProtoTracer mProtoTracer; @Mock ShellCommandHandler mShellCommandHandler; @Mock ShellExecutor mSysUiMainExecutor; @@ -81,7 +84,8 @@ public class WMShellTest extends SysuiTestCase { Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor, mNavigationModeController, - mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor); + mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle, + mSysUiMainExecutor); } @Test @@ -106,6 +110,7 @@ public class WMShellTest extends SysuiTestCase { verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class)); verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class)); verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class)); + verify(mOneHanded).registerEventCallback(any(OneHandedEventCallback.class)); } @Test diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml index b9c5f1ded3d5..8d0227eed875 100644 --- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml @@ -25,9 +25,9 @@ <!-- Max((28 + 20), 0) = 48 --> <dimen name="status_bar_height_landscape">48dp</dimen> <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) --> - <dimen name="quick_qs_offset_height">28dp</dimen> + <dimen name="quick_qs_offset_height">48dp</dimen> <!-- Total height of QQS (quick_qs_offset_height + 128) --> - <dimen name="quick_qs_total_height">156dp</dimen> + <dimen name="quick_qs_total_height">176dp</dimen> <dimen name="waterfall_display_left_edge_size">20dp</dimen> <dimen name="waterfall_display_top_edge_size">0dp</dimen> diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 7eecc453a8ce..241a0dbac758 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2892,7 +2892,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // In case user assigned magnification to the given shortcut. if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) { final boolean enabled = !getFullScreenMagnificationController().isMagnifying(displayId); - logAccessibilityShortcutActivated(MAGNIFICATION_COMPONENT_NAME, shortcutType, enabled); + logAccessibilityShortcutActivated(mContext, MAGNIFICATION_COMPONENT_NAME, shortcutType, + enabled); sendAccessibilityButtonToInputFilter(displayId); return; } @@ -2907,7 +2908,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // In case user assigned an accessibility shortcut target to the given shortcut. if (performAccessibilityShortcutTargetActivity(displayId, targetComponentName)) { - logAccessibilityShortcutActivated(targetComponentName, shortcutType); + logAccessibilityShortcutActivated(mContext, targetComponentName, shortcutType); return; } // in case user assigned an accessibility service to the given shortcut. @@ -2930,12 +2931,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub featureInfo.getSettingKey(), mCurrentUserId); // Assuming that the default state will be to have the feature off if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) { - logAccessibilityShortcutActivated(assignedTarget, shortcutType, /* serviceEnabled= */ - true); + logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType, + /* serviceEnabled= */ true); setting.write(featureInfo.getSettingOnValue()); } else { - logAccessibilityShortcutActivated(assignedTarget, shortcutType, /* serviceEnabled= */ - false); + logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType, + /* serviceEnabled= */ false); setting.write(featureInfo.getSettingOffValue()); } return true; @@ -2997,13 +2998,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY) || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) { if (serviceConnection == null) { - logAccessibilityShortcutActivated(assignedTarget, - shortcutType, /* serviceEnabled= */ true); + logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType, + /* serviceEnabled= */ true); enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId); } else { - logAccessibilityShortcutActivated(assignedTarget, - shortcutType, /* serviceEnabled= */ false); + logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType, + /* serviceEnabled= */ false); disableAccessibilityServiceLocked(assignedTarget, mCurrentUserId); } return true; @@ -3024,8 +3025,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } // ServiceConnection means service enabled. - logAccessibilityShortcutActivated(assignedTarget, shortcutType, /* serviceEnabled= */ - true); + logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType, + /* serviceEnabled= */ true); serviceConnection.notifyAccessibilityButtonClickedLocked(displayId); return true; } diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java index 3310cb4e3e79..ea2c7d2a41e9 100644 --- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java +++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java @@ -31,9 +31,9 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.view.InputDevice; import android.view.MotionEvent; +import android.view.WindowManagerPolicyConstants; import com.android.internal.os.SomeArgs; -import com.android.server.policy.WindowManagerPolicy; import java.util.ArrayList; import java.util.Arrays; @@ -122,6 +122,12 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement return; } cancelAnyPendingInjectedEvents(); + // The events injected from outside of system_server are not trusted. Remove the flag to + // prevent accessibility service from impersonating a real input device. + policyFlags &= ~WindowManagerPolicyConstants.FLAG_INPUTFILTER_TRUSTED; + // Indicate that the input event is injected from accessibility, to let applications + // distinguish it from events injected by other means. + policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY; sendMotionEventToNext(event, rawEvent, policyFlags); } @@ -156,7 +162,9 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement return false; } MotionEvent motionEvent = (MotionEvent) message.obj; - sendMotionEventToNext(motionEvent, motionEvent, WindowManagerPolicy.FLAG_PASS_TO_USER); + sendMotionEventToNext(motionEvent, motionEvent, + WindowManagerPolicyConstants.FLAG_PASS_TO_USER + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY); boolean isEndOfSequence = message.arg1 != 0; if (isEndOfSequence) { notifyService(mServiceInterfaceForCurrentGesture, mSequencesInProgress.get(0), true); @@ -308,7 +316,8 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement MotionEvent cancelEvent = obtainMotionEvent(now, now, MotionEvent.ACTION_CANCEL, getLastTouchPoints(), 1); sendMotionEventToNext(cancelEvent, cancelEvent, - WindowManagerPolicy.FLAG_PASS_TO_USER); + WindowManagerPolicyConstants.FLAG_PASS_TO_USER + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY); mOpenGesturesInProgress.put(source, false); } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 320047fec66b..078d908684bc 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2648,6 +2648,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": " + viewState.getStateAsString()); } + // Fix to always let standard autofill start. + // Sometimes activity contain IMPORTANT_FOR_AUTOFILL_NO fields which marks session as + // augmentedOnly, but other fields are still fillable by standard autofill. + mSessionFlags.mAugmentedAutofillOnly = false; requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags); return true; } @@ -2847,12 +2851,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Slog.d(TAG, "trigger augmented autofill."); triggerAugmentedAutofillLocked(flags); } else { - if (sDebug) Slog.d(TAG, "skip augmented autofill for same view."); + if (sDebug) { + Slog.d(TAG, "skip augmented autofill for same view: " + + "same view entered"); + } } return; } else if (mSessionFlags.mAugmentedAutofillOnly && isSameViewEntered) { // Regular autofill is disabled. - if (sDebug) Slog.d(TAG, "skip augmented autofill for same view."); + if (sDebug) { + Slog.d(TAG, "skip augmented autofill for same view: " + + "standard autofill disabled."); + } return; } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 83dfe8ed2576..906edb30ff12 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -454,19 +454,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind }).cancelTimeout(); }, FgThread.getExecutor()).whenComplete(uncheckExceptions((association, err) -> { - - final long callingIdentity = Binder.clearCallingIdentity(); - try { - if (err == null) { - addAssociation(association); - } else { - Slog.e(LOG_TAG, "Failed to discover device(s)", err); - callback.onFailure("No devices found: " + err.getMessage()); - } - cleanup(); - } finally { - Binder.restoreCallingIdentity(callingIdentity); + if (err == null) { + addAssociation(association); + } else { + Slog.e(LOG_TAG, "Failed to discover device(s)", err); + callback.onFailure("No devices found: " + err.getMessage()); } + cleanup(); })); } @@ -759,9 +753,19 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } return notMatch; })); + restartBleScan(); } void onAssociationPreRemove(Association association) { + if (association.isNotifyOnDeviceNearby()) { + ServiceConnector<ICompanionDeviceService> serviceConnector = + mDeviceListenerServiceConnectors.forUser(association.getUserId()) + .get(association.getPackageName()); + if (serviceConnector != null) { + serviceConnector.unbind(); + } + } + String deviceProfile = association.getDeviceProfile(); if (deviceProfile != null) { Association otherAssociationWithDeviceProfile = find( @@ -793,16 +797,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } } - - if (association.isNotifyOnDeviceNearby()) { - ServiceConnector<ICompanionDeviceService> serviceConnector = - mDeviceListenerServiceConnectors.forUser(association.getUserId()) - .get(association.getPackageName()); - if (serviceConnector != null) { - serviceConnector.unbind(); - restartBleScan(); - } - } } private void updateSpecialAccessPermissionForAssociatedPackage(Association association) { diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index a231de3178f9..2a634ebdd555 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -375,6 +375,9 @@ public class AccountManagerService // Cancel account request notification if a permission was preventing the account access mPackageManager.addOnPermissionsChangeListener( (int uid) -> { + // Permission changes cause requires updating accounts cache. + AccountManager.invalidateLocalAccountsDataCaches(); + Account[] accounts = null; String[] packageNames = mPackageManager.getPackagesForUid(uid); if (packageNames != null) { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 0802123b39da..5e388d94869d 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1877,7 +1877,6 @@ public final class ActiveServices { active.mNumActive++; } r.isForeground = true; - r.mLogEntering = true; // The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could // be deferred, make a copy of mAllowStartForeground and // mAllowWhileInUsePermissionInFgs. @@ -1903,6 +1902,9 @@ public final class ActiveServices { AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); registerAppOpCallbackLocked(r); mAm.updateForegroundServiceUsageStats(r.name, r.userId, true); + logFGSStateChangeLocked(r, + FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER, + 0); } // Even if the service is already a FGS, we need to update the notification, // so we need to call it again. @@ -1958,6 +1960,7 @@ public final class ActiveServices { FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT, r.mFgsExitTime > r.mFgsEnterTime ? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0); + r.mFgsNotificationWasDeferred = false; resetFgsRestrictionLocked(r); mAm.updateForegroundServiceUsageStats(r.name, r.userId, false); if (r.app != null) { @@ -2162,6 +2165,7 @@ public final class ActiveServices { } r.fgDisplayTime = when; r.mFgsNotificationDeferred = true; + r.mFgsNotificationWasDeferred = true; r.mFgsNotificationShown = false; mPendingFgsNotifications.add(r); if (DEBUG_FOREGROUND_SERVICE) { @@ -2205,11 +2209,6 @@ public final class ActiveServices { Slog.d(TAG_SERVICE, " - service no longer running/fg, ignoring"); } } - // Regardless of whether we needed to post the notification or the - // service is no longer running, we may not have logged its FGS - // transition yet depending on the timing and API sequence that led - // to this point - so make sure to do so. - maybeLogFGSStateEnteredLocked(r); } } if (DEBUG_FOREGROUND_SERVICE) { @@ -2252,16 +2251,6 @@ public final class ActiveServices { } } - private void maybeLogFGSStateEnteredLocked(ServiceRecord r) { - if (r.mLogEntering) { - logFGSStateChangeLocked(r, - FrameworkStatsLog - .FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER, - 0); - r.mLogEntering = false; - } - } - /** * Callback from NotificationManagerService whenever it posts a notification * associated with a foreground service. This is the unified handling point @@ -2280,9 +2269,7 @@ public final class ActiveServices { && id == sr.foregroundId && sr.appInfo.packageName.equals(pkg)) { // Found it. If 'shown' is false, it means that the notification - // subsystem will not be displaying it yet, so all we do is log - // the "fgs entered" transition noting deferral, then we're done. - maybeLogFGSStateEnteredLocked(sr); + // subsystem will not be displaying it yet. if (shown) { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG_SERVICE, "Notification shown; canceling deferral of " @@ -4240,6 +4227,7 @@ public final class ActiveServices { r.isForeground = false; r.foregroundId = 0; r.foregroundNoti = null; + r.mFgsNotificationWasDeferred = false; resetFgsRestrictionLocked(r); // Clear start entries. @@ -6263,7 +6251,7 @@ public final class ActiveServices { ? r.mRecentCallerApplicationInfo.targetSdkVersion : 0, r.mInfoTempFgsAllowListReason != null ? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID, - r.mFgsNotificationDeferred, + r.mFgsNotificationWasDeferred, r.mFgsNotificationShown, durationMs, r.mStartForegroundCount, diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 4e6e91ac7b5d..08f6f1e6e46e 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; @@ -78,6 +79,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.BatteryUsageStatsProvider; +import com.android.internal.os.BatteryUsageStatsStore; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; @@ -124,10 +126,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub Watchdog.Monitor { static final String TAG = "BatteryStatsService"; static final boolean DBG = false; + private static final boolean BATTERY_USAGE_STORE_ENABLED = true; private static IBatteryStats sService; final BatteryStatsImpl mStats; + private final BatteryUsageStatsStore mBatteryUsageStatsStore; private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider; private final Context mContext; private final BatteryExternalStatsWorker mWorker; @@ -348,7 +352,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.setPowerProfileLocked(new PowerProfile(context)); mStats.startTrackingSystemServerCpuTime(); - mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats); + if (BATTERY_USAGE_STORE_ENABLED) { + mBatteryUsageStatsStore = + new BatteryUsageStatsStore(context, mStats, systemDir, mHandler); + } else { + mBatteryUsageStatsStore = null; + } + mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats, + mBatteryUsageStatsStore); } public void publish() { @@ -752,6 +763,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL, null, // use default PullAtomMetadata values BackgroundThread.getExecutor(), pullAtomCallback); + statsManager.setPullAtomCallback( + FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET, + null, // use default PullAtomMetadata values + BackgroundThread.getExecutor(), pullAtomCallback); } /** StatsPullAtomCallback for pulling BatteryUsageStats data. */ @@ -768,6 +783,17 @@ public final class BatteryStatsService extends IBatteryStats.Stub new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(); bus = getBatteryUsageStats(List.of(powerProfileQuery)).get(0); break; + case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET: + final long sessionStart = mBatteryUsageStatsStore + .getLastBatteryUsageStatsBeforeResetAtomPullTimestamp(); + final long sessionEnd = mStats.getStartClockTime(); + final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder() + .aggregateSnapshots(sessionStart, sessionEnd) + .build(); + bus = getBatteryUsageStats(List.of(query)).get(0); + mBatteryUsageStatsStore + .setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd); + break; default: throw new UnsupportedOperationException("Unknown tagId=" + atomTag); } @@ -2499,6 +2525,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub * @hide */ public CellularBatteryStats getCellularBatteryStats() { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS) == PERMISSION_DENIED) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BATTERY_STATS, null); + } + // Wait for the completion of pending works if there is any awaitCompletion(); synchronized (mStats) { @@ -2511,6 +2543,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub * @hide */ public WifiBatteryStats getWifiBatteryStats() { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS) == PERMISSION_DENIED) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BATTERY_STATS, null); + } + // Wait for the completion of pending works if there is any awaitCompletion(); synchronized (mStats) { @@ -2523,6 +2561,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub * @hide */ public GpsBatteryStats getGpsBatteryStats() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null); + // Wait for the completion of pending works if there is any awaitCompletion(); synchronized (mStats) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 62286379ba33..aef402ac3213 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -328,7 +328,8 @@ public class OomAdjuster { final int index = mCache.indexOfKey(app.packageName); Pair<Boolean, WeakReference<ApplicationInfo>> p; if (index < 0) { - p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app), + p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId, + app), new WeakReference<>(app)); mCache.put(app.packageName, p); return p.first; @@ -338,7 +339,8 @@ public class OomAdjuster { return p.first; } // Cache is invalid, regenerate it - p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app), + p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId, + app), new WeakReference<>(app)); mCache.setValueAt(index, p); return p.first; diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 3ba07af710c2..141f081ab9a7 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -109,7 +109,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN boolean fgWaiting; // is a timeout for going foreground already scheduled? boolean isNotAppComponentUsage; // is service binding not considered component/package usage? boolean isForeground; // is service currently in foreground mode? - boolean mLogEntering; // need to report fgs transition once deferral policy is known int foregroundId; // Notification ID of last foreground req. Notification foregroundNoti; // Notification record of foreground state. long fgDisplayTime; // time at which the FGS notification should become visible @@ -167,8 +166,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN long mFgsEnterTime = 0; // The uptime when the service exits FGS state. long mFgsExitTime = 0; - // FGS notification was deferred. + // FGS notification is deferred. boolean mFgsNotificationDeferred; + // FGS notification was deferred. + boolean mFgsNotificationWasDeferred; // FGS notification was shown before the FGS finishes, or it wasn't deferred in the first place. boolean mFgsNotificationShown; diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 104bc9b2c8d1..99a33e4462e2 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -848,7 +848,7 @@ public class AppOpsService extends IAppOpsService.Stub { return mAttributionFlags; } - /** @return attributoin chiang id for the access */ + /** @return attribution chain id for the access */ public int getAttributionChainId() { return mAttributionChainId; } @@ -912,7 +912,8 @@ public class AppOpsService extends IAppOpsService.Stub { proxyAttributionTag, uidState, flags); mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName, - tag, uidState, flags, accessTime); + tag, uidState, flags, accessTime, AppOpsManager.ATTRIBUTION_FLAGS_NONE, + AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); } /** @@ -1053,9 +1054,9 @@ public class AppOpsService extends IAppOpsService.Stub { event.numUnfinishedStarts++; if (isStarted) { - // TODO: Consider storing the attribution chain flags and id mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, - parent.packageName, tag, uidState, flags, startTime); + parent.packageName, tag, uidState, flags, startTime, attributionFlags, + attributionChainId); } } @@ -1112,7 +1113,8 @@ public class AppOpsService extends IAppOpsService.Stub { mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid, parent.packageName, tag, event.getUidState(), - event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration()); + event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(), + event.getAttributionFlags(), event.getAttributionChainId()); if (!isPausing) { mInProgressStartOpEventPool.release(event); @@ -1215,7 +1217,8 @@ public class AppOpsService extends IAppOpsService.Stub { event.mStartElapsedTime = SystemClock.elapsedRealtime(); event.mStartTime = startTime; mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, - parent.packageName, tag, event.mUidState, event.mFlags, startTime); + parent.packageName, tag, event.mUidState, event.mFlags, startTime, + event.getAttributionFlags(), event.getAttributionChainId()); if (shouldSendActive) { scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName, tag, true, event.getAttributionFlags(), event.getAttributionChainId()); diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java index 10cfddfb3ae4..49469cc8a597 100644 --- a/services/core/java/com/android/server/appop/DiscreteRegistry.java +++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java @@ -26,6 +26,7 @@ import static android.app.AppOpsManager.OP_FINE_LOCATION; import static android.app.AppOpsManager.OP_FLAGS_ALL; import static android.app.AppOpsManager.OP_FLAG_SELF; import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED; +import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE; @@ -156,8 +157,11 @@ final class DiscreteRegistry { private static final String ATTR_NOTE_DURATION = "nd"; private static final String ATTR_UID_STATE = "us"; private static final String ATTR_FLAGS = "f"; + private static final String ATTR_ATTRIBUTION_FLAGS = "af"; + private static final String ATTR_CHAIN_ID = "ci"; - private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED; + private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED + | OP_FLAG_TRUSTED_PROXY; // Lock for read/write access to on disk state private final Object mOnDiskLock = new Object(); @@ -227,13 +231,14 @@ final class DiscreteRegistry { void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime, - long accessDuration) { + long accessDuration, @AppOpsManager.AttributionFlags int attributionFlags, + int attributionChainId) { if (!isDiscreteOp(op, flags)) { return; } synchronized (mInMemoryLock) { mDiscreteOps.addDiscreteAccess(op, uid, packageName, attributionTag, flags, uidState, - accessTime, accessDuration); + accessTime, accessDuration, attributionFlags, attributionChainId); } } @@ -383,9 +388,10 @@ final class DiscreteRegistry { void addDiscreteAccess(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, @AppOpsManager.OpFlags int flags, - @AppOpsManager.UidState int uidState, long accessTime, long accessDuration) { + @AppOpsManager.UidState int uidState, long accessTime, long accessDuration, + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags, - uidState, accessTime, accessDuration); + uidState, accessTime, accessDuration, attributionFlags, attributionChainId); } private void filter(long beginTimeMillis, long endTimeMillis, @@ -613,9 +619,10 @@ final class DiscreteRegistry { void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, - long accessTime, long accessDuration) { + long accessTime, long accessDuration, + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { getOrCreateDiscretePackageOps(packageName).addDiscreteAccess(op, attributionTag, flags, - uidState, accessTime, accessDuration); + uidState, accessTime, accessDuration, attributionFlags, attributionChainId); } private DiscretePackageOps getOrCreateDiscretePackageOps(String packageName) { @@ -680,9 +687,10 @@ final class DiscreteRegistry { void addDiscreteAccess(int op, @Nullable String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, - long accessTime, long accessDuration) { + long accessTime, long accessDuration, + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { getOrCreateDiscreteOp(op).addDiscreteAccess(attributionTag, flags, uidState, accessTime, - accessDuration); + accessDuration, attributionFlags, attributionChainId); } void merge(DiscretePackageOps other) { @@ -823,37 +831,39 @@ final class DiscreteRegistry { for (int j = 0; j < n; j++) { DiscreteOpEvent event = list.get(j); list.set(j, new DiscreteOpEvent(event.mNoteTime - offset, event.mNoteDuration, - event.mUidState, event.mOpFlag)); + event.mUidState, event.mOpFlag, event.mAttributionFlags, + event.mAttributionChainId)); } } } void addDiscreteAccess(@Nullable String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, - long accessTime, long accessDuration) { + long accessTime, long accessDuration, + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList( attributionTag); - accessTime = accessTime / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization; - accessDuration = accessDuration == -1 ? -1 - : (accessDuration + sDiscreteHistoryQuantization - 1) - / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization; int nAttributedOps = attributedOps.size(); int i = nAttributedOps; for (; i > 0; i--) { DiscreteOpEvent previousOp = attributedOps.get(i - 1); - if (previousOp.mNoteTime < accessTime) { + if (discretizeTimeStamp(previousOp.mNoteTime) < discretizeTimeStamp(accessTime)) { break; } - if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) { - if (accessDuration != previousOp.mNoteDuration) { + if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState + && previousOp.mAttributionFlags == attributionFlags + && previousOp.mAttributionChainId == attributionChainId) { + if (discretizeDuration(accessDuration) != discretizeDuration( + previousOp.mNoteDuration)) { break; } else { return; } } } - attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags)); + attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags, + attributionFlags, attributionChainId)); } private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) { @@ -875,7 +885,8 @@ final class DiscreteRegistry { for (int j = 0; j < nEvents; j++) { DiscreteOpEvent event = events.get(j); result.addDiscreteAccess(op, uid, packageName, tag, event.mUidState, - event.mOpFlag, event.mNoteTime, event.mNoteDuration); + event.mOpFlag, discretizeTimeStamp(event.mNoteTime), + discretizeDuration(event.mNoteDuration)); } } } @@ -932,11 +943,15 @@ final class DiscreteRegistry { -1); int uidState = parser.getAttributeInt(null, ATTR_UID_STATE); int opFlags = parser.getAttributeInt(null, ATTR_FLAGS); + int attributionFlags = parser.getAttributeInt(null, + ATTR_ATTRIBUTION_FLAGS, AppOpsManager.ATTRIBUTION_FLAGS_NONE); + int attributionChainId = parser.getAttributeInt(null, ATTR_CHAIN_ID, + AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); if (noteTime + noteDuration < beginTimeMillis) { continue; } DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration, - uidState, opFlags); + uidState, opFlags, attributionFlags, attributionChainId); events.add(event); } } @@ -952,13 +967,18 @@ final class DiscreteRegistry { final long mNoteDuration; final @AppOpsManager.UidState int mUidState; final @AppOpsManager.OpFlags int mOpFlag; + final @AppOpsManager.AttributionFlags int mAttributionFlags; + final int mAttributionChainId; DiscreteOpEvent(long noteTime, long noteDuration, @AppOpsManager.UidState int uidState, - @AppOpsManager.OpFlags int opFlag) { + @AppOpsManager.OpFlags int opFlag, + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { mNoteTime = noteTime; mNoteDuration = noteDuration; mUidState = uidState; mOpFlag = opFlag; + mAttributionFlags = attributionFlags; + mAttributionChainId = attributionChainId; } private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, @@ -969,13 +989,19 @@ final class DiscreteRegistry { pw.print("-"); pw.print(flagsToString(mOpFlag)); pw.print("] at "); - date.setTime(mNoteTime); + date.setTime(discretizeTimeStamp(mNoteTime)); pw.print(sdf.format(date)); if (mNoteDuration != -1) { pw.print(" for "); - pw.print(mNoteDuration); + pw.print(discretizeDuration(mNoteDuration)); pw.print(" milliseconds "); } + if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) { + pw.print(" attribution flags="); + pw.print(mAttributionFlags); + pw.print(" with chainId="); + pw.print(mAttributionChainId); + } pw.println(); } @@ -984,6 +1010,12 @@ final class DiscreteRegistry { if (mNoteDuration != -1) { out.attributeLong(null, ATTR_NOTE_DURATION, mNoteDuration); } + if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) { + out.attributeInt(null, ATTR_ATTRIBUTION_FLAGS, mAttributionFlags); + } + if (mAttributionChainId != AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE) { + out.attributeInt(null, ATTR_CHAIN_ID, mAttributionChainId); + } out.attributeInt(null, ATTR_UID_STATE, mUidState); out.attributeInt(null, ATTR_FLAGS, mOpFlag); } @@ -1055,6 +1087,16 @@ final class DiscreteRegistry { return true; } + private static long discretizeTimeStamp(long timeStamp) { + return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization; + + } + + private static long discretizeDuration(long duration) { + return duration == -1 ? -1 : (duration + sDiscreteHistoryQuantization - 1) + / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization; + } + void setDebugMode(boolean debugMode) { this.mDebugMode = debugMode; } diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 8b72be78a7f6..dd5df503d936 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -487,7 +487,8 @@ final class HistoricalRegistry { void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, - long accessTime) { + long accessTime, @AppOpsManager.AttributionFlags int attributionFlags, + int attributionChainId) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { @@ -499,7 +500,7 @@ final class HistoricalRegistry { attributionTag, uidState, flags, 1); mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag, - flags, uidState, accessTime, -1); + flags, uidState, accessTime, -1, attributionFlags, attributionChainId); } } } @@ -521,7 +522,8 @@ final class HistoricalRegistry { void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, - long eventStartTime, long increment) { + long eventStartTime, long increment, + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { @@ -532,7 +534,8 @@ final class HistoricalRegistry { System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName, attributionTag, uidState, flags, increment); mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag, - flags, uidState, eventStartTime, increment); + flags, uidState, eventStartTime, increment, attributionFlags, + attributionChainId); } } } 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 0ba731ee8b4b..cb966e7b47a9 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 @@ -59,11 +59,13 @@ public class FaceDetectClient extends AcquisitionClient<ISession> implements Det @Override protected void stopHalOperation() { - try { - mCancellationSignal.cancel(); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - mCallback.onClientFinished(this, false /* success */); + if (mCancellationSignal != null) { + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + mCallback.onClientFinished(this, false /* success */); + } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java index 66142bff0453..f1f94564c35d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java @@ -46,6 +46,8 @@ final class AidlConversionUtils { return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_REMOVE; } else if (aidlError == Error.VENDOR) { return BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; + } else if (aidlError == Error.BAD_CALIBRATION) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_BAD_CALIBARTION; } else { return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNKNOWN; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java index 160736433112..8d777e1c2787 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java @@ -124,10 +124,12 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId()); pm.incrementAuthForUser(getTargetUserId(), authenticated); - try { - getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when sending onDetected", e); + if (getListener() != null) { + try { + getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when sending onDetected", e); + } } } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 6dca00191b24..c2375351aee9 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -257,8 +257,8 @@ final class CompatConfig { addChange(c); } c.addPackageOverride(packageName, overrides, allowedState, versionCode); - invalidateCache(); } + invalidateCache(); return alreadyKnown; } @@ -379,9 +379,9 @@ final class CompatConfig { CompatChange change = mChanges.valueAt(i); removeOverrideUnsafe(change, packageName, versionCode); } - saveOverrides(); - invalidateCache(); } + saveOverrides(); + invalidateCache(); } /** @@ -626,7 +626,18 @@ final class CompatConfig { if (mOverridesFile == null) { return; } + Overrides overrides = new Overrides(); synchronized (mChanges) { + List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides(); + for (int idx = 0; idx < mChanges.size(); ++idx) { + CompatChange c = mChanges.valueAt(idx); + ChangeOverrides changeOverrides = c.saveOverrides(); + if (changeOverrides != null) { + changeOverridesList.add(changeOverrides); + } + } + } + synchronized (mOverridesFile) { // Create the file if it doesn't already exist try { mOverridesFile.createNewFile(); @@ -636,15 +647,6 @@ final class CompatConfig { } try (PrintWriter out = new PrintWriter(mOverridesFile)) { XmlWriter writer = new XmlWriter(out); - Overrides overrides = new Overrides(); - List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides(); - for (int idx = 0; idx < mChanges.size(); ++idx) { - CompatChange c = mChanges.valueAt(idx); - ChangeOverrides changeOverrides = c.saveOverrides(); - if (changeOverrides != null) { - changeOverridesList.add(changeOverrides); - } - } XmlWriter.write(writer, overrides); } catch (IOException e) { Slog.e(TAG, e.toString()); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index ddac19b540ae..7148becca281 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1250,6 +1250,8 @@ public class Vpn { updateState(DetailedState.CONNECTING, "agentConnect"); final NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig.Builder() + .setLegacyType(ConnectivityManager.TYPE_VPN) + .setLegacyTypeName("VPN") .setBypassableVpn(mConfig.allowBypass && !mLockdown) .build(); diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 170564d145dc..88f88a8439cc 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -19,8 +19,11 @@ package com.android.server.display; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation; import android.os.Environment; import android.os.PowerManager; +import android.text.TextUtils; import android.util.MathUtils; import android.util.Slog; import android.util.Spline; @@ -34,6 +37,7 @@ import com.android.server.display.config.HbmTiming; import com.android.server.display.config.HighBrightnessMode; import com.android.server.display.config.NitsMap; import com.android.server.display.config.Point; +import com.android.server.display.config.RefreshRateRange; import com.android.server.display.config.SensorDetails; import com.android.server.display.config.XmlParser; @@ -79,10 +83,13 @@ public class DisplayDeviceConfig { private final Context mContext; // The details of the ambient light sensor associated with this display. - private final SensorIdentifier mAmbientLightSensor = new SensorIdentifier(); + private final SensorData mAmbientLightSensor = new SensorData(); // The details of the proximity sensor associated with this display. - private final SensorIdentifier mProximitySensor = new SensorIdentifier(); + private final SensorData mProximitySensor = new SensorData(); + + private final List<RefreshRateLimitation> mRefreshRateLimitations = + new ArrayList<>(2 /*initialCapacity*/); // Nits and backlight values that are loaded from either the display device config file, or // config.xml. These are the raw values and just used for the dumpsys @@ -113,6 +120,7 @@ public class DisplayDeviceConfig { private List<String> mQuirks; private boolean mIsHighBrightnessModeEnabled = false; private HighBrightnessModeData mHbmData; + private String mLoadedFrom = null; private DisplayDeviceConfig(Context context) { mContext = context; @@ -273,11 +281,11 @@ public class DisplayDeviceConfig { return mBrightnessRampSlowIncrease; } - SensorIdentifier getAmbientLightSensor() { + SensorData getAmbientLightSensor() { return mAmbientLightSensor; } - SensorIdentifier getProximitySensor() { + SensorData getProximitySensor() { return mProximitySensor; } @@ -303,10 +311,15 @@ public class DisplayDeviceConfig { return hbmData; } + public List<RefreshRateLimitation> getRefreshRateLimitations() { + return mRefreshRateLimitations; + } + @Override public String toString() { String str = "DisplayDeviceConfig{" - + "mBacklight=" + Arrays.toString(mBacklight) + + "mLoadedFrom=" + mLoadedFrom + + ", mBacklight=" + Arrays.toString(mBacklight) + ", mNits=" + Arrays.toString(mNits) + ", mRawBacklight=" + Arrays.toString(mRawBacklight) + ", mRawNits=" + Arrays.toString(mRawNits) @@ -325,6 +338,7 @@ public class DisplayDeviceConfig { + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease + ", mAmbientLightSensor=" + mAmbientLightSensor + ", mProximitySensor=" + mProximitySensor + + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray()) + "}"; return str; } @@ -336,9 +350,8 @@ public class DisplayDeviceConfig { final String filename = String.format(CONFIG_FILE_FORMAT, suffix); final File filePath = Environment.buildPath( baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename); - if (filePath.exists()) { - final DisplayDeviceConfig config = new DisplayDeviceConfig(context); - config.initFromFile(filePath); + final DisplayDeviceConfig config = new DisplayDeviceConfig(context); + if (config.initFromFile(filePath)) { return config; } return null; @@ -356,15 +369,15 @@ public class DisplayDeviceConfig { return config; } - private void initFromFile(File configFile) { + private boolean initFromFile(File configFile) { if (!configFile.exists()) { // Display configuration files aren't required to exist. - return; + return false; } if (!configFile.isFile()) { Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping"); - return; + return false; } try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { @@ -385,6 +398,8 @@ public class DisplayDeviceConfig { Slog.e(TAG, "Encountered an error while reading/parsing display config file: " + configFile, e); } + mLoadedFrom = configFile.toString(); + return true; } private void initFromGlobalXml() { @@ -395,10 +410,12 @@ public class DisplayDeviceConfig { loadBrightnessRampsFromConfigXml(); loadAmbientLightSensorFromConfigXml(); setProxSensorUnspecified(); + mLoadedFrom = "<config.xml>"; } private void initFromDefaultValues() { // Set all to basic values + mLoadedFrom = "Static values"; mBacklightMinimum = PowerManager.BRIGHTNESS_MIN; mBacklightMaximum = PowerManager.BRIGHTNESS_MAX; mBrightnessDefault = BRIGHTNESS_DEFAULT; @@ -640,6 +657,13 @@ public class DisplayDeviceConfig { mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000; mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000; mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000; + final RefreshRateRange rr = hbm.getRefreshRate_all(); + if (rr != null) { + final float min = rr.getMinimum().floatValue(); + final float max = rr.getMaximum().floatValue(); + mRefreshRateLimitations.add(new RefreshRateLimitation( + DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max)); + } } } @@ -689,6 +713,11 @@ public class DisplayDeviceConfig { if (sensorDetails != null) { mAmbientLightSensor.type = sensorDetails.getType(); mAmbientLightSensor.name = sensorDetails.getName(); + final RefreshRateRange rr = sensorDetails.getRefreshRate(); + if (rr != null) { + mAmbientLightSensor.minRefreshRate = rr.getMinimum().floatValue(); + mAmbientLightSensor.maxRefreshRate = rr.getMaximum().floatValue(); + } } else { loadAmbientLightSensorFromConfigXml(); } @@ -704,22 +733,42 @@ public class DisplayDeviceConfig { if (sensorDetails != null) { mProximitySensor.name = sensorDetails.getName(); mProximitySensor.type = sensorDetails.getType(); + final RefreshRateRange rr = sensorDetails.getRefreshRate(); + if (rr != null) { + mProximitySensor.minRefreshRate = rr.getMinimum().floatValue(); + mProximitySensor.maxRefreshRate = rr.getMaximum().floatValue(); + } } else { setProxSensorUnspecified(); } } - static class SensorIdentifier { + static class SensorData { public String type; public String name; + public float minRefreshRate = 0.0f; + public float maxRefreshRate = Float.POSITIVE_INFINITY; @Override public String toString() { return "Sensor{" - + "type: \"" + type + "\"" - + ", name: \"" + name + "\"" + + "type: " + type + + ", name: " + name + + ", refreshRateRange: [" + minRefreshRate + ", " + maxRefreshRate + "]" + "} "; } + + /** + * @return True if the sensor matches both the specified name and type, or one if only + * one is specified (not-empty). Always returns false if both parameters are null or empty. + */ + public boolean matches(String sensorName, String sensorType) { + final boolean isNameSpecified = !TextUtils.isEmpty(sensorName); + final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType); + return (isNameSpecified || isTypeSpecified) + && (!isNameSpecified || sensorName.equals(name)) + && (!isTypeSpecified || sensorType.equals(type)); + } } /** @@ -771,7 +820,7 @@ public class DisplayDeviceConfig { + ", transition: " + transitionPoint + ", timeWindow: " + timeWindowMillis + "ms" + ", timeMax: " + timeMaxMillis + "ms" - + ", timeMin: " + timeMinMillis + + ", timeMin: " + timeMinMillis + "ms" + "} "; } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index fe5ee78bca31..182a038d10f7 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -50,6 +50,7 @@ import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.ColorSpace; import android.graphics.Point; +import android.hardware.Sensor; import android.hardware.SensorManager; import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.AmbientBrightnessDayStats; @@ -62,6 +63,8 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; +import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; @@ -119,6 +122,8 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; +import com.android.server.display.DisplayDeviceConfig.SensorData; +import com.android.server.display.utils.SensorUtils; import com.android.server.wm.SurfaceAnimationThread; import com.android.server.wm.WindowManagerInternal; @@ -126,6 +131,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; @@ -2107,6 +2113,11 @@ public final class DisplayManagerService extends SystemService { DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED); } + private DisplayDevice getDeviceForDisplayLocked(int displayId) { + final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId); + return display == null ? null : display.getPrimaryDisplayDeviceLocked(); + } + private final class DisplayManagerHandler extends Handler { public DisplayManagerHandler(Looper looper) { super(looper, null, true /*async*/); @@ -3257,6 +3268,53 @@ public final class DisplayManagerService extends SystemService { public int getRefreshRateSwitchingType() { return getRefreshRateSwitchingTypeInternal(); } + + @Override + public RefreshRateRange getRefreshRateForDisplayAndSensor(int displayId, String sensorName, + String sensorType) { + final SensorManager sensorManager; + synchronized (mSyncRoot) { + sensorManager = mSensorManager; + } + if (sensorManager == null) { + return null; + } + + // Verify that the specified sensor exists. + final Sensor sensor = SensorUtils.findSensor(sensorManager, sensorType, sensorName, + SensorUtils.NO_FALLBACK); + if (sensor == null) { + return null; + } + + synchronized (mSyncRoot) { + final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId); + final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); + if (device == null) { + return null; + } + final DisplayDeviceConfig config = device.getDisplayDeviceConfig(); + SensorData sensorData = config.getProximitySensor(); + if (sensorData.matches(sensorName, sensorType)) { + return new RefreshRateRange(sensorData.minRefreshRate, + sensorData.maxRefreshRate); + } + } + return null; + } + + @Override + public List<RefreshRateLimitation> getRefreshRateLimitations(int displayId) { + final DisplayDeviceConfig config; + synchronized (mSyncRoot) { + final DisplayDevice device = getDeviceForDisplayLocked(displayId); + if (device == null) { + return null; + } + config = device.getDisplayDeviceConfig(); + } + return config.getRefreshRateLimitations(); + } } class DesiredDisplayModeSpecsObserver diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 07d13c251f26..83fc9665f192 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; @@ -26,7 +28,11 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.fingerprint.IUdfpsHbmListener; import android.net.Uri; import android.os.Handler; @@ -51,6 +57,8 @@ import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; +import com.android.server.sensors.SensorManagerInternal; +import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.utils.DeviceConfigInterface; @@ -85,8 +93,7 @@ public class DisplayModeDirector { private static final int INVALID_DISPLAY_MODE_ID = -1; - // The tolerance within which we consider something approximately equals. - private static final float FLOAT_TOLERANCE = 0.01f; + private static final float FLOAT_TOLERANCE = RefreshRateRange.FLOAT_TOLERANCE; private final Object mLock = new Object(); private final Context mContext; @@ -98,6 +105,8 @@ public class DisplayModeDirector { private final SettingsObserver mSettingsObserver; private final DisplayObserver mDisplayObserver; private final UdfpsObserver mUdfpsObserver; + private final SensorObserver mSensorObserver; + private final HbmObserver mHbmObserver; private final DeviceConfigInterface mDeviceConfig; private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; @@ -123,7 +132,7 @@ public class DisplayModeDirector { private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS; public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { - this(context, handler, new RealInjector()); + this(context, handler, new RealInjector(context)); } public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler, @@ -139,6 +148,13 @@ public class DisplayModeDirector { mDisplayObserver = new DisplayObserver(context, handler); mBrightnessObserver = new BrightnessObserver(context, handler); mUdfpsObserver = new UdfpsObserver(); + final BallotBox ballotBox = (displayId, priority, vote) -> { + synchronized (mLock) { + updateVoteLocked(displayId, priority, vote); + } + }; + mSensorObserver = new SensorObserver(context, ballotBox); + mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler()); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); mDeviceConfig = injector.getDeviceConfig(); mAlwaysRespectAppRequest = false; @@ -155,6 +171,8 @@ public class DisplayModeDirector { mSettingsObserver.observe(); mDisplayObserver.observe(); mBrightnessObserver.observe(sensorManager); + mSensorObserver.observe(); + mHbmObserver.observe(); synchronized (mLock) { // We may have a listener already registered before the call to start, so go ahead and // notify them to pick up our newly initialized state. @@ -585,6 +603,8 @@ public class DisplayModeDirector { mAppRequestObserver.dumpLocked(pw); mBrightnessObserver.dumpLocked(pw); mUdfpsObserver.dumpLocked(pw); + mSensorObserver.dumpLocked(pw); + mHbmObserver.dumpLocked(pw); } } @@ -768,66 +788,6 @@ public class DisplayModeDirector { } /** - * Information about the min and max refresh rate DM would like to set the display to. - */ - public static final class RefreshRateRange { - /** - * The lowest desired refresh rate. - */ - public float min; - /** - * The highest desired refresh rate. - */ - public float max; - - public RefreshRateRange() {} - - public RefreshRateRange(float min, float max) { - if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) { - Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " - + min + " " + max); - this.min = this.max = 0; - return; - } - if (min > max) { - // Min and max are within epsilon of each other, but in the wrong order. - float t = min; - min = max; - max = t; - } - this.min = min; - this.max = max; - } - - /** - * Checks whether the two objects have the same values. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof RefreshRateRange)) { - return false; - } - - RefreshRateRange refreshRateRange = (RefreshRateRange) other; - return (min == refreshRateRange.min && max == refreshRateRange.max); - } - - @Override - public int hashCode() { - return Objects.hash(min, max); - } - - @Override - public String toString() { - return "(" + min + " " + max + ")"; - } - } - - /** * Information about the desired display mode to be set by the system. Includes the base * mode ID and the primary and app request refresh rate ranges. * @@ -987,9 +947,16 @@ public class DisplayModeDirector { // user seeing the display flickering when the switches occur. public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8; + // High-brightness-mode may need a specific range of refresh-rates to function properly. + public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 9; + + // The proximity sensor needs the refresh rate to be locked in order to function, so this is + // set to a high priority. + public static final int PRIORITY_PROXIMITY = 10; + // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order // to function, so this needs to be the highest priority of all votes. - public static final int PRIORITY_UDFPS = 9; + public static final int PRIORITY_UDFPS = 11; // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. @@ -1066,27 +1033,30 @@ public class DisplayModeDirector { public static String priorityToString(int priority) { switch (priority) { + case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE: + return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE"; + case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE: + return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE"; + case PRIORITY_APP_REQUEST_SIZE: + return "PRIORITY_APP_REQUEST_SIZE"; case PRIORITY_DEFAULT_REFRESH_RATE: return "PRIORITY_DEFAULT_REFRESH_RATE"; case PRIORITY_FLICKER_REFRESH_RATE: return "PRIORITY_FLICKER_REFRESH_RATE"; case PRIORITY_FLICKER_REFRESH_RATE_SWITCH: return "PRIORITY_FLICKER_REFRESH_RATE_SWITCH"; - case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: - return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE"; - case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE: - return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE"; - case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE: - return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE"; - case PRIORITY_APP_REQUEST_SIZE: - return "PRIORITY_APP_REQUEST_SIZE"; - case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE: - return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE"; + case PRIORITY_HIGH_BRIGHTNESS_MODE: + return "PRIORITY_HIGH_BRIGHTNESS_MODE"; + case PRIORITY_PROXIMITY: + return "PRIORITY_PROXIMITY"; case PRIORITY_LOW_POWER_MODE: return "PRIORITY_LOW_POWER_MODE"; case PRIORITY_UDFPS: return "PRIORITY_UDFPS"; - + case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: + return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE"; + case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE: + return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE"; default: return Integer.toString(priority); } @@ -2142,6 +2112,131 @@ public class DisplayModeDirector { } } + private static class SensorObserver implements ProximityActiveListener { + private static final String PROXIMITY_SENSOR_NAME = null; + private static final String PROXIMITY_SENSOR_TYPE = Sensor.STRING_TYPE_PROXIMITY; + + private final BallotBox mBallotBox; + private final Context mContext; + + private DisplayManager mDisplayManager; + private DisplayManagerInternal mDisplayManagerInternal; + private boolean mIsProxActive = false; + + SensorObserver(Context context, BallotBox ballotBox) { + mContext = context; + mBallotBox = ballotBox; + } + + @Override + public void onProximityActive(boolean isActive) { + if (mIsProxActive != isActive) { + mIsProxActive = isActive; + recalculateVotes(); + } + } + + public void observe() { + mDisplayManager = mContext.getSystemService(DisplayManager.class); + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); + + final SensorManagerInternal sensorManager = + LocalServices.getService(SensorManagerInternal.class); + sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this); + } + + private void recalculateVotes() { + final Display[] displays = mDisplayManager.getDisplays(); + for (Display d : displays) { + int displayId = d.getDisplayId(); + Vote vote = null; + if (mIsProxActive) { + final RefreshRateRange rate = + mDisplayManagerInternal.getRefreshRateForDisplayAndSensor( + displayId, PROXIMITY_SENSOR_NAME, PROXIMITY_SENSOR_TYPE); + if (rate != null) { + vote = Vote.forRefreshRates(rate.min, rate.max); + } + } + mBallotBox.vote(displayId, Vote.PRIORITY_PROXIMITY, vote); + } + } + + void dumpLocked(PrintWriter pw) { + pw.println(" SensorObserver"); + pw.println(" mIsProxActive=" + mIsProxActive); + } + } + + /** + * Listens to DisplayManager for HBM status and applies any refresh-rate restrictions for + * HBM that are associated with that display. Restrictions are retrieved from + * DisplayManagerInternal but originate in the display-device-config file. + */ + private static class HbmObserver implements DisplayManager.DisplayListener { + private final BallotBox mBallotBox; + private final Handler mHandler; + private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray(); + private final Injector mInjector; + + private DisplayManagerInternal mDisplayManagerInternal; + + HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) { + mInjector = injector; + mBallotBox = ballotBox; + mHandler = handler; + } + + public void observe() { + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); + mInjector.registerDisplayListener(this, mHandler, + DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS + | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED); + } + + @Override + public void onDisplayAdded(int displayId) {} + + @Override + public void onDisplayRemoved(int displayId) { + mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null); + } + + @Override + public void onDisplayChanged(int displayId) { + final BrightnessInfo info = mInjector.getBrightnessInfo(displayId); + if (info == null) { + // Display no longer there. Assume we'll get an onDisplayRemoved very soon. + return; + } + final boolean isHbmEnabled = + info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; + if (isHbmEnabled == mHbmEnabled.get(displayId)) { + // no change, ignore. + return; + } + Vote vote = null; + mHbmEnabled.put(displayId, isHbmEnabled); + if (isHbmEnabled) { + final List<RefreshRateLimitation> limits = + mDisplayManagerInternal.getRefreshRateLimitations(displayId); + for (int i = 0; limits != null && i < limits.size(); i++) { + final RefreshRateLimitation limitation = limits.get(i); + if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) { + vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max); + break; + } + } + } + mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote); + } + + void dumpLocked(PrintWriter pw) { + pw.println(" HbmObserver"); + pw.println(" mHbmEnabled: " + mHbmEnabled); + } + } + private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener { public DeviceConfigDisplaySettings() { } @@ -2296,10 +2391,21 @@ public class DisplayModeDirector { void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer); + + void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener, + Handler handler, long flags); + + BrightnessInfo getBrightnessInfo(int displayId); } @VisibleForTesting static class RealInjector implements Injector { + private final Context mContext; + private DisplayManager mDisplayManager; + + RealInjector(Context context) { + mContext = context; + } @Override @NonNull @@ -2326,6 +2432,31 @@ public class DisplayModeDirector { cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, observer, UserHandle.USER_SYSTEM); } + + @Override + public void registerDisplayListener(DisplayManager.DisplayListener listener, + Handler handler, long flags) { + getDisplayManager().registerDisplayListener(listener, handler, flags); + } + + @Override + public BrightnessInfo getBrightnessInfo(int displayId) { + final Display display = getDisplayManager().getDisplay(displayId); + if (display != null) { + return display.getBrightnessInfo(); + } + return null; + } + + private DisplayManager getDisplayManager() { + if (mDisplayManager == null) { + mDisplayManager = mContext.getSystemService(DisplayManager.class); + } + return mDisplayManager; + } } + interface BallotBox { + void vote(int displayId, int priority, Vote vote); + } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 1b50f0388f9d..555add4b027f 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -48,7 +48,6 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; -import android.text.TextUtils; import android.util.Log; import android.util.MathUtils; import android.util.Slog; @@ -65,13 +64,13 @@ import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; +import com.android.server.display.utils.SensorUtils; import com.android.server.display.whitebalance.DisplayWhiteBalanceController; import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory; import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; -import java.util.List; /** * Controls the power state of the display. @@ -586,26 +585,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits); } - private Sensor findSensor(String sensorType, String sensorName, int fallbackType, - boolean useFallback) { - final boolean isNameSpecified = !TextUtils.isEmpty(sensorName); - final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType); - List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); - if (isNameSpecified || isTypeSpecified) { - for (Sensor sensor : sensors) { - if ((!isNameSpecified || sensorName.equals(sensor.getName())) - && (!isTypeSpecified || sensorType.equals(sensor.getStringType()))) { - return sensor; - } - } - } - if (useFallback) { - return mSensorManager.getDefaultSensor(fallbackType); - } else { - return null; - } - } - /** * Returns true if the proximity sensor screen-off function is available. */ @@ -1654,24 +1633,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void loadAmbientLightSensor() { - DisplayDeviceConfig.SensorIdentifier lightSensor = - mDisplayDeviceConfig.getAmbientLightSensor(); - String lightSensorName = lightSensor.name; - String lightSensorType = lightSensor.type; - mLightSensor = findSensor(lightSensorType, lightSensorName, Sensor.TYPE_LIGHT, - mDisplayId == Display.DEFAULT_DISPLAY); + DisplayDeviceConfig.SensorData lightSensor = mDisplayDeviceConfig.getAmbientLightSensor(); + final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY + ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK; + mLightSensor = SensorUtils.findSensor(mSensorManager, lightSensor.type, lightSensor.name, + fallbackType); } private void loadProximitySensor() { if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) { return; } - final DisplayDeviceConfig.SensorIdentifier proxSensor = + final DisplayDeviceConfig.SensorData proxSensor = mDisplayDeviceConfig.getProximitySensor(); - final String proxSensorName = proxSensor.name; - final String proxSensorType = proxSensor.type; - mProximitySensor = findSensor(proxSensorType, proxSensorName, Sensor.TYPE_PROXIMITY, - mDisplayId == Display.DEFAULT_DISPLAY); + final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY + ? Sensor.TYPE_PROXIMITY : SensorUtils.NO_FALLBACK; + mProximitySensor = SensorUtils.findSensor(mSensorManager, proxSensor.type, proxSensor.name, + fallbackType); if (mProximitySensor != null) { mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(), TYPICAL_PROXIMITY_THRESHOLD); diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index b9487779dfd3..57a8c4b998ae 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -22,6 +22,7 @@ import android.os.IBinder; import android.os.PowerManager; import android.os.SystemClock; import android.util.Slog; +import android.util.TimeUtils; import android.view.SurfaceControlHdrLayerInfoListener; import com.android.internal.annotations.VisibleForTesting; @@ -189,6 +190,10 @@ class HighBrightnessModeController { } void dump(PrintWriter pw) { + mHandler.runWithScissors(() -> dumpLocal(pw), 1000); + } + + private void dumpLocal(PrintWriter pw) { pw.println("HighBrightnessModeController:"); pw.println(" mCurrentMin=" + getCurrentBrightnessMin()); pw.println(" mCurrentMax=" + getCurrentBrightnessMax()); @@ -202,6 +207,29 @@ class HighBrightnessModeController { pw.println(" mIsHdrLayerPresent=" + mIsHdrLayerPresent); pw.println(" mBrightnessMin=" + mBrightnessMin); pw.println(" mBrightnessMax=" + mBrightnessMax); + pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis)); + pw.println(" mEvents="); + final long currentTime = mClock.uptimeMillis(); + long lastStartTime = currentTime; + if (mRunningStartTimeMillis != -1) { + lastStartTime = dumpHbmEvent(pw, new HbmEvent(mRunningStartTimeMillis, currentTime)); + } + for (HbmEvent event : mEvents) { + if (lastStartTime > event.endTimeMillis) { + pw.println(" event: [normal brightness]: " + + TimeUtils.formatDuration(lastStartTime - event.endTimeMillis)); + } + lastStartTime = dumpHbmEvent(pw, event); + } + } + + private long dumpHbmEvent(PrintWriter pw, HbmEvent event) { + final long duration = event.endTimeMillis - event.startTimeMillis; + pw.println(" event: [" + + TimeUtils.formatUptime(event.startTimeMillis) + ", " + + TimeUtils.formatUptime(event.endTimeMillis) + "] (" + + TimeUtils.formatDuration(duration) + ")"); + return event.startTimeMillis; } private boolean isCurrentlyAllowed() { diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java index 6db75eb80aea..a7e1a2876f81 100644 --- a/services/core/java/com/android/server/display/WifiDisplayController.java +++ b/services/core/java/com/android/server/display/WifiDisplayController.java @@ -550,11 +550,6 @@ final class WifiDisplayController implements DumpUtils.Dump { private void disconnect() { mDesiredDevice = null; - mWifiP2pManager = null; - if (null != mWifiP2pChannel) { - mWifiP2pChannel.close(); - mWifiP2pChannel = null; - } updateConnection(); } diff --git a/services/core/java/com/android/server/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java new file mode 100644 index 000000000000..cb40b406899f --- /dev/null +++ b/services/core/java/com/android/server/display/utils/SensorUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.utils; + +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.text.TextUtils; + +import java.util.List; + +/** + * Provides utility methods for dealing with sensors. + */ +public class SensorUtils { + public static final int NO_FALLBACK = 0; + + /** + * Finds the specified sensor by type and name using SensorManager. + */ + public static Sensor findSensor(SensorManager sensorManager, String sensorType, + String sensorName, int fallbackType) { + final boolean isNameSpecified = !TextUtils.isEmpty(sensorName); + final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType); + if (isNameSpecified || isTypeSpecified) { + final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); + for (Sensor sensor : sensors) { + if ((!isNameSpecified || sensorName.equals(sensor.getName())) + && (!isTypeSpecified || sensorType.equals(sensor.getStringType()))) { + return sensor; + } + } + } + if (fallbackType != NO_FALLBACK) { + return sensorManager.getDefaultSensor(fallbackType); + } + + return null; + } + +} diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 2ed160a33af4..a086bdaa6ec9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -2580,7 +2580,8 @@ public class HdmiControlService extends SystemService { return null; } - private void addHdmiControlStatusChangeListener( + @VisibleForTesting + void addHdmiControlStatusChangeListener( final IHdmiControlStatusChangeListener listener) { final HdmiControlStatusChangeListenerRecord record = new HdmiControlStatusChangeListenerRecord(listener); @@ -2916,13 +2917,17 @@ public class HdmiControlService extends SystemService { } else { mIsCecAvailable = true; } + if (!listeners.isEmpty()) { + invokeHdmiControlStatusChangeListenerLocked(listeners, + isEnabled, mIsCecAvailable); + } } }); } else { mIsCecAvailable = false; - } - if (!listeners.isEmpty()) { - invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, mIsCecAvailable); + if (!listeners.isEmpty()) { + invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, mIsCecAvailable); + } } } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index b9e97e89051b..aeb1893c78b6 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -65,7 +65,7 @@ import android.location.LastLocationRequest; import android.location.Location; import android.location.LocationManager; import android.location.LocationManagerInternal; -import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener; +import android.location.LocationManagerInternal.LocationPackageTagsListener; import android.location.LocationProvider; import android.location.LocationRequest; import android.location.LocationTime; @@ -93,6 +93,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; +import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.location.eventlog.LocationEventLog; @@ -259,7 +260,7 @@ public class LocationManagerService extends ILocationManager.Stub implements new CopyOnWriteArrayList<>(); @GuardedBy("mLock") - @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener; + @Nullable LocationPackageTagsListener mLocationTagsChangedListener; LocationManagerService(Context context, Injector injector) { mContext = context.createAttributionContext(ATTRIBUTION_TAG); @@ -1363,32 +1364,28 @@ public class LocationManagerService extends ILocationManager.Stub implements refreshAppOpsRestrictions(UserHandle.USER_ALL); } - OnProviderLocationTagsChangeListener listener; - synchronized (mLock) { - listener = mOnProviderLocationTagsChangeListener; - } - - if (listener != null) { - if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags) - || !Objects.equals(oldState.identity, newState.identity)) { - if (oldState.identity != null) { - listener.onLocationTagsChanged( - new LocationManagerInternal.LocationTagInfo( - oldState.identity.getUid(), - oldState.identity.getPackageName(), - Collections.emptySet())); - } - if (newState.identity != null) { - ArraySet<String> attributionTags = new ArraySet<>( - newState.extraAttributionTags.size() + 1); - attributionTags.addAll(newState.extraAttributionTags); - attributionTags.add(newState.identity.getAttributionTag()); - - listener.onLocationTagsChanged( - new LocationManagerInternal.LocationTagInfo( - newState.identity.getUid(), - newState.identity.getPackageName(), - attributionTags)); + if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags) + || !Objects.equals(oldState.identity, newState.identity)) { + // since we're potentially affecting the tag lists for two different uids, acquire the + // lock to ensure providers cannot change while we're looping over the providers + // multiple times, which could lead to inconsistent results. + synchronized (mLock) { + LocationPackageTagsListener listener = mLocationTagsChangedListener; + if (listener != null) { + int oldUid = oldState.identity != null ? oldState.identity.getUid() : -1; + int newUid = newState.identity != null ? newState.identity.getUid() : -1; + if (oldUid != -1) { + PackageTagsList tags = calculateAppOpsLocationSourceTags(oldUid); + FgThread.getHandler().post( + () -> listener.onLocationPackageTagsChanged(oldUid, tags)); + } + // if the new app id is the same as the old app id, no need to invoke the + // listener twice, it's already been taken care of + if (newUid != -1 && newUid != oldUid) { + PackageTagsList tags = calculateAppOpsLocationSourceTags(newUid); + FgThread.getHandler().post( + () -> listener.onLocationPackageTagsChanged(newUid, tags)); + } } } } @@ -1436,6 +1433,31 @@ public class LocationManagerService extends ILocationManager.Stub implements userId); } + PackageTagsList calculateAppOpsLocationSourceTags(int uid) { + PackageTagsList.Builder builder = new PackageTagsList.Builder(); + for (LocationProviderManager manager : mProviderManagers) { + AbstractLocationProvider.State managerState = manager.getState(); + if (managerState.identity == null) { + continue; + } + if (managerState.identity.getUid() != uid) { + continue; + } + + builder.add(managerState.identity.getPackageName(), managerState.extraAttributionTags); + if (managerState.extraAttributionTags.isEmpty() + || managerState.identity.getAttributionTag() != null) { + builder.add(managerState.identity.getPackageName(), + managerState.identity.getAttributionTag()); + } else { + Log.e(TAG, manager.getName() + " provider has specified a null attribution tag and " + + "a non-empty set of extra attribution tags - dropping the null " + + "attribution tag"); + } + } + return builder.build(); + } + private class LocalService extends LocationManagerInternal { LocalService() {} @@ -1506,10 +1528,29 @@ public class LocationManagerService extends ILocationManager.Stub implements } @Override - public void setOnProviderLocationTagsChangeListener( - @Nullable OnProviderLocationTagsChangeListener listener) { + public void setLocationPackageTagsListener( + @Nullable LocationPackageTagsListener listener) { synchronized (mLock) { - mOnProviderLocationTagsChangeListener = listener; + mLocationTagsChangedListener = listener; + + // calculate initial tag list and send to listener + if (listener != null) { + ArraySet<Integer> uids = new ArraySet<>(mProviderManagers.size()); + for (LocationProviderManager manager : mProviderManagers) { + CallerIdentity identity = manager.getIdentity(); + if (identity != null) { + uids.add(identity.getUid()); + } + } + + for (int uid : uids) { + PackageTagsList tags = calculateAppOpsLocationSourceTags(uid); + if (!tags.isEmpty()) { + FgThread.getHandler().post( + () -> listener.onLocationPackageTagsChanged(uid, tags)); + } + } + } } } } diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java index 1da45bd49767..eb7b77a2234f 100644 --- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java @@ -263,10 +263,10 @@ public abstract class AbstractLocationProvider { } /** - * The current allowed state of this provider. + * The current state of the provider. */ - public final boolean isAllowed() { - return mInternalState.get().state.allowed; + public final State getState() { + return mInternalState.get().state; } /** @@ -277,13 +277,6 @@ public abstract class AbstractLocationProvider { } /** - * The current provider properties of this provider. - */ - public final @Nullable ProviderProperties getProperties() { - return mInternalState.get().state.properties; - } - - /** * Call this method to report a change in provider properties. */ protected void setProperties(@Nullable ProviderProperties properties) { @@ -291,13 +284,6 @@ public abstract class AbstractLocationProvider { } /** - * The current identity of this provider. - */ - public final @Nullable CallerIdentity getIdentity() { - return mInternalState.get().state.identity; - } - - /** * Call this method to report a change in the provider's identity. */ protected void setIdentity(@Nullable CallerIdentity identity) { diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 4f8b87b51294..8d335b83d99c 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -1407,12 +1407,16 @@ public class LocationProviderManager extends return mName; } + public AbstractLocationProvider.State getState() { + return mProvider.getState(); + } + public @Nullable CallerIdentity getIdentity() { - return mProvider.getIdentity(); + return mProvider.getState().identity; } public @Nullable ProviderProperties getProperties() { - return mProvider.getProperties(); + return mProvider.getState().properties; } public boolean hasProvider() { @@ -2403,7 +2407,7 @@ public class LocationProviderManager extends Preconditions.checkArgument(userId >= 0); boolean enabled = mState == STATE_STARTED - && mProvider.isAllowed() + && mProvider.getState().allowed && mSettingsHelper.isLocationEnabled(userId); int index = mEnabled.indexOfKey(userId); diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java index 021e8dbdb44e..22295fe7ba28 100644 --- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java @@ -21,9 +21,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import android.annotation.Nullable; import android.location.Location; import android.location.LocationResult; -import android.location.provider.ProviderProperties; import android.location.provider.ProviderRequest; -import android.location.util.identity.CallerIdentity; import android.os.Bundle; import com.android.internal.annotations.GuardedBy; @@ -32,7 +30,6 @@ import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collections; -import java.util.Set; /** * Represents a location provider that may switch between a mock implementation and a real @@ -290,21 +287,21 @@ public class MockableLocationProvider extends AbstractLocationProvider { Preconditions.checkState(!Thread.holdsLock(mOwnerLock)); AbstractLocationProvider provider; + State providerState; synchronized (mOwnerLock) { provider = mProvider; - pw.println("allowed=" + isAllowed()); - CallerIdentity identity = getIdentity(); - if (identity != null) { - pw.println("identity=" + identity); - } - Set<String> extraAttributionTags = getExtraAttributionTags(); - if (!extraAttributionTags.isEmpty()) { - pw.println("extra attribution tags=" + extraAttributionTags); - } - ProviderProperties properties = getProperties(); - if (properties != null) { - pw.println("properties=" + properties); - } + providerState = getState(); + } + + pw.println("allowed=" + providerState.allowed); + if (providerState.identity != null) { + pw.println("identity=" + providerState.identity); + } + if (!providerState.extraAttributionTags.isEmpty()) { + pw.println("extra attribution tags=" + providerState.extraAttributionTags); + } + if (providerState.properties != null) { + pw.println("properties=" + providerState.properties); } if (provider != null) { diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java index 2caad5065840..41e067e57190 100644 --- a/services/core/java/com/android/server/notification/BubbleExtractor.java +++ b/services/core/java/com/android/server/notification/BubbleExtractor.java @@ -167,20 +167,20 @@ public class BubbleExtractor implements NotificationSignalExtractor { // TODO: check the shortcut intent / ensure it can show in activity view return true; } - return canLaunchInActivityView(mContext, metadata.getIntent(), pkg); + return canLaunchInTaskView(mContext, metadata.getIntent(), pkg); } /** * Whether an intent is properly configured to display in an {@link - * android.app.ActivityView} for bubbling. + * com.android.wm.shell.TaskView} for bubbling. * * @param context the context to use. * @param pendingIntent the pending intent of the bubble. * @param packageName the notification package name for this bubble. */ - // Keep checks in sync with BubbleController#canLaunchInActivityView. + // Keep checks in sync with BubbleController#canLaunchInTaskView. @VisibleForTesting - protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent, + protected boolean canLaunchInTaskView(Context context, PendingIntent pendingIntent, String packageName) { if (pendingIntent == null) { Slog.w(TAG, "Unable to create bubble -- no intent"); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b26485b5aad9..a3f3a3a503c8 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4785,7 +4785,7 @@ public class NotificationManagerService extends SystemService { } @Override - public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) { + public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) { Objects.requireNonNull(automaticZenRule, "automaticZenRule is null"); Objects.requireNonNull(automaticZenRule.getName(), "Name is null"); if (automaticZenRule.getOwner() == null @@ -4794,6 +4794,7 @@ public class NotificationManagerService extends SystemService { "Rule must have a conditionproviderservice and/or configuration activity"); } Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null"); + checkCallerIsSameApp(pkg); if (automaticZenRule.getZenPolicy() != null && automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) { throw new IllegalArgumentException("ZenPolicy is only applicable to " @@ -4801,7 +4802,7 @@ public class NotificationManagerService extends SystemService { } enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule"); - return mZenModeHelper.addAutomaticZenRule(automaticZenRule, + return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule, "addAutomaticZenRule"); } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 4cb6c3ba20d8..a98f113a9d57 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -307,7 +307,8 @@ public class ZenModeHelper { return null; } - public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) { + public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule, + String reason) { if (!isSystemRule(automaticZenRule)) { PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner()); if (component == null) { @@ -340,7 +341,7 @@ public class ZenModeHelper { } newConfig = mConfig.copy(); ZenRule rule = new ZenRule(); - populateZenRule(automaticZenRule, rule, true); + populateZenRule(pkg, automaticZenRule, rule, true); newConfig.automaticRules.put(rule.id, rule); if (setConfigLocked(newConfig, reason, rule.component, true)) { return rule.id; @@ -376,7 +377,7 @@ public class ZenModeHelper { ? AUTOMATIC_RULE_STATUS_ENABLED : AUTOMATIC_RULE_STATUS_DISABLED); } - populateZenRule(automaticZenRule, rule, false); + populateZenRule(rule.pkg, automaticZenRule, rule, false); return setConfigLocked(newConfig, reason, rule.component, true); } } @@ -585,7 +586,8 @@ public class ZenModeHelper { return null; } - private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) { + private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule, + boolean isNew) { rule.name = automaticZenRule.getName(); rule.condition = null; rule.conditionId = automaticZenRule.getConditionId(); @@ -600,9 +602,7 @@ public class ZenModeHelper { rule.id = ZenModeConfig.newRuleId(); rule.creationTime = System.currentTimeMillis(); rule.component = automaticZenRule.getOwner(); - rule.pkg = (rule.component != null) - ? rule.component.getPackageName() - : rule.configurationActivity.getPackageName(); + rule.pkg = pkg; } if (rule.enabled != automaticZenRule.isEnabled()) { @@ -611,10 +611,13 @@ public class ZenModeHelper { } protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) { - return new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity, + AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component, + rule.configurationActivity, rule.conditionId, rule.zenPolicy, NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled, rule.creationTime); + azr.setPackageName(rule.pkg); + return azr; } public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) { diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index 293c59d1831d..346f3113f25f 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AppOpsManager; +import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; @@ -31,6 +32,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserHandle; import android.os.UserManager; import android.telephony.TelephonyManager; import android.util.ArraySet; @@ -81,7 +83,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { == BugreportParams.BUGREPORT_MODE_TELEPHONY /* checkCarrierPrivileges */); final long identity = Binder.clearCallingIdentity(); try { - ensureIsPrimaryUser(); + ensureUserCanTakeBugReport(bugreportMode); } finally { Binder.restoreCallingIdentity(identity); } @@ -166,11 +168,12 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } /** - * Validates that the current user is the primary user. + * Validates that the current user is the primary user or when bugreport is requested remotely + * and current user is affiliated user. * * @throws IllegalArgumentException if the current user is not the primary user */ - private void ensureIsPrimaryUser() { + private void ensureUserCanTakeBugReport(int bugreportMode) { UserInfo currentUser = null; try { currentUser = ActivityManager.getService().getCurrentUser(); @@ -186,11 +189,40 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { logAndThrow("No primary user. Only primary user is allowed to take bugreports."); } if (primaryUser.id != currentUser.id) { + if (bugreportMode == BugreportParams.BUGREPORT_MODE_REMOTE + && isCurrentUserAffiliated(currentUser.id)) { + return; + } logAndThrow("Current user not primary user. Only primary user" + " is allowed to take bugreports."); } } + /** + * Returns {@code true} if the device has device owner and the current user is affiliated + * with the device owner. + */ + private boolean isCurrentUserAffiliated(int currentUserId) { + DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); + int deviceOwnerUid = dpm.getDeviceOwnerUserId(); + if (deviceOwnerUid == UserHandle.USER_NULL) { + return false; + } + + int callingUserId = UserHandle.getUserId(Binder.getCallingUid()); + + Slog.i(TAG, "callingUid: " + callingUserId + " deviceOwnerUid: " + deviceOwnerUid + + " currentUserId: " + currentUserId); + + if (callingUserId != deviceOwnerUid) { + logAndThrow("Caller is not device owner on provisioned device."); + } + if (!dpm.isAffiliatedUser(currentUserId)) { + logAndThrow("Current user is not affiliated to the device owner."); + } + return true; + } + @GuardedBy("mLock") private void startBugreportLocked(int callingUid, String callingPackage, FileDescriptor bugreportFd, FileDescriptor screenshotFd, diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 06ff69176bb7..d1d1eb00b114 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static android.os.UserHandle.USER_ALL; import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; @@ -685,7 +686,7 @@ public class AppsFilter implements Watchable, Snappable { synchronized (mCacheLock) { if (mShouldFilterCache != null) { updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting, - settings, users, settings.size()); + settings, users, USER_ALL, settings.size()); if (additionalChangedPackages != null) { for (int index = 0; index < additionalChangedPackages.size(); index++) { String changedPackage = additionalChangedPackages.valueAt(index); @@ -698,7 +699,8 @@ public class AppsFilter implements Watchable, Snappable { } updateShouldFilterCacheForPackage(mShouldFilterCache, null, - changedPkgSetting, settings, users, settings.size()); + changedPkgSetting, settings, users, USER_ALL, + settings.size()); } } } // else, rebuild entire cache when system is ready @@ -830,24 +832,51 @@ public class AppsFilter implements Watchable, Snappable { } } } - private void updateEntireShouldFilterCache() { + updateEntireShouldFilterCache(USER_ALL); + } + + private void updateEntireShouldFilterCache(int subjectUserId) { mStateProvider.runWithState((settings, users) -> { + int userId = subjectUserId; + if (!ArrayUtils.contains(users, subjectUserId)) { + Slog.e(TAG, "We encountered a new user that isn't a member of known users, " + + "updating the whole cache"); + userId = USER_ALL; + } WatchedSparseBooleanMatrix cache = - updateEntireShouldFilterCacheInner(settings, users); + updateEntireShouldFilterCacheInner(settings, users, userId); synchronized (mCacheLock) { + if (userId != USER_ALL) { + // if we're only updating a single user id, we need to copy over the prior + // cached values for the other users. + int[] uids = mShouldFilterCache.keys(); + for (int i = 0; i < uids.length; i++) { + int uid1 = uids[i]; + if (UserHandle.getUserId(uid1) == userId) { + continue; + } + for (int j = 0; j < uids.length; j++) { + int uid2 = uids[j]; + if (UserHandle.getUserId(uid2) == userId) { + continue; + } + cache.setValueAt(uid1, uid2, mShouldFilterCache.valueAt(uid1, uid2)); + } + } + } mShouldFilterCache = cache; } }); } private WatchedSparseBooleanMatrix updateEntireShouldFilterCacheInner( - ArrayMap<String, PackageSetting> settings, UserInfo[] users) { + ArrayMap<String, PackageSetting> settings, UserInfo[] users, int subjectUserId) { WatchedSparseBooleanMatrix cache = new WatchedSparseBooleanMatrix(users.length * settings.size()); for (int i = settings.size() - 1; i >= 0; i--) { updateShouldFilterCacheForPackage(cache, - null /*skipPackage*/, settings.valueAt(i), settings, users, i); + null /*skipPackage*/, settings.valueAt(i), settings, users, subjectUserId, i); } return cache; } @@ -868,8 +897,8 @@ public class AppsFilter implements Watchable, Snappable { packagesCache.put(settings.keyAt(i), pkg); } }); - WatchedSparseBooleanMatrix cache = - updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0]); + WatchedSparseBooleanMatrix cache = updateEntireShouldFilterCacheInner( + settingsCopy, usersRef[0], USER_ALL); boolean[] changed = new boolean[1]; // We have a cache, let's make sure the world hasn't changed out from under us. mStateProvider.runWithState((settings, users) -> { @@ -899,10 +928,10 @@ public class AppsFilter implements Watchable, Snappable { }); } - public void onUsersChanged() { + public void onUserCreated(int newUserId) { synchronized (mCacheLock) { if (mShouldFilterCache != null) { - updateEntireShouldFilterCache(); + updateEntireShouldFilterCache(newUserId); onChanged(); } } @@ -913,7 +942,7 @@ public class AppsFilter implements Watchable, Snappable { if (mShouldFilterCache != null) { mStateProvider.runWithState((settings, users) -> { updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */, - settings.get(packageName), settings, users, + settings.get(packageName), settings, users, USER_ALL, settings.size() /*maxIndex*/); }); } @@ -922,7 +951,7 @@ public class AppsFilter implements Watchable, Snappable { private void updateShouldFilterCacheForPackage(WatchedSparseBooleanMatrix cache, @Nullable String skipPackageName, PackageSetting subjectSetting, ArrayMap<String, - PackageSetting> allSettings, UserInfo[] allUsers, int maxIndex) { + PackageSetting> allSettings, UserInfo[] allUsers, int subjectUserId, int maxIndex) { for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) { PackageSetting otherSetting = allSettings.valueAt(i); if (subjectSetting.appId == otherSetting.appId) { @@ -932,25 +961,34 @@ public class AppsFilter implements Watchable, Snappable { if (subjectSetting.name == skipPackageName || otherSetting.name == skipPackageName) { continue; } - final int userCount = allUsers.length; - final int appxUidCount = userCount * allSettings.size(); - for (int su = 0; su < userCount; su++) { - int subjectUser = allUsers[su].id; - for (int ou = 0; ou < userCount; ou++) { - int otherUser = allUsers[ou].id; - int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId); - int otherUid = UserHandle.getUid(otherUser, otherSetting.appId); - cache.put(subjectUid, otherUid, - shouldFilterApplicationInternal( - subjectUid, subjectSetting, otherSetting, otherUser)); - cache.put(otherUid, subjectUid, - shouldFilterApplicationInternal( - otherUid, otherSetting, subjectSetting, subjectUser)); + if (subjectUserId == USER_ALL) { + for (int su = 0; su < allUsers.length; su++) { + updateShouldFilterCacheForUser(cache, subjectSetting, allUsers, otherSetting, + allUsers[su].id); } + } else { + updateShouldFilterCacheForUser(cache, subjectSetting, allUsers, otherSetting, + subjectUserId); } } } + private void updateShouldFilterCacheForUser(WatchedSparseBooleanMatrix cache, + PackageSetting subjectSetting, UserInfo[] allUsers, PackageSetting otherSetting, + int subjectUserId) { + for (int ou = 0; ou < allUsers.length; ou++) { + int otherUser = allUsers[ou].id; + int subjectUid = UserHandle.getUid(subjectUserId, subjectSetting.appId); + int otherUid = UserHandle.getUid(otherUser, otherSetting.appId); + cache.put(subjectUid, otherUid, + shouldFilterApplicationInternal( + subjectUid, subjectSetting, otherSetting, otherUser)); + cache.put(otherUid, subjectUid, + shouldFilterApplicationInternal( + otherUid, otherSetting, subjectSetting, subjectUserId)); + } + } + private static boolean isSystemSigned(@NonNull PackageParser.SigningDetails sysSigningDetails, PackageSetting pkgSetting) { return pkgSetting.isSystem() @@ -1145,7 +1183,7 @@ public class AppsFilter implements Watchable, Snappable { continue; } updateShouldFilterCacheForPackage(mShouldFilterCache, setting.name, - siblingSetting, settings, users, settings.size()); + siblingSetting, settings, users, USER_ALL, settings.size()); } } @@ -1162,7 +1200,7 @@ public class AppsFilter implements Watchable, Snappable { } updateShouldFilterCacheForPackage(mShouldFilterCache, null, - changedPkgSetting, settings, users, settings.size()); + changedPkgSetting, settings, users, USER_ALL, settings.size()); } } } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index cd383b9d1d7a..8fd545f271f3 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -80,6 +80,17 @@ public class Installer extends SystemService { /** Indicates that dexopt may be run with different performance / priority tuned for restore */ public static final int DEXOPT_FOR_RESTORE = 1 << 13; // TODO(b/135202722): remove + /** The result of the profile analysis indicating that the app should be optimized. */ + public static final int PROFILE_ANALYSIS_OPTIMIZE = 1; + /** The result of the profile analysis indicating that the app should not be optimized. */ + public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA = 2; + /** + * The result of the profile analysis indicating that the app should not be optimized because + * the profiles are empty. + */ + public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3; + + public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE; public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL; @@ -496,9 +507,18 @@ public class Installer extends SystemService { } } - public boolean mergeProfiles(int uid, String packageName, String profileName) + /** + * Analyzes the ART profiles of the given package, possibly merging the information + * into the reference profile. Returns whether or not we should optimize the package + * based on how much information is in the profile. + * + * @return one of {@link #PROFILE_ANALYSIS_OPTIMIZE}, + * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA}, + * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES} + */ + public int mergeProfiles(int uid, String packageName, String profileName) throws InstallerException { - if (!checkBeforeRemote()) return false; + if (!checkBeforeRemote()) return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; try { return mInstalld.mergeProfiles(uid, packageName, profileName); } catch (Exception e) { @@ -645,13 +665,17 @@ public class Installer extends SystemService { } } - public void deleteOdex(String apkPath, String instructionSet, String outputPath) + /** + * Deletes the optimized artifacts generated by ART and returns the number + * of freed bytes. + */ + public long deleteOdex(String apkPath, String instructionSet, String outputPath) throws InstallerException { - if (!checkBeforeRemote()) return; + if (!checkBeforeRemote()) return -1; BlockGuard.getVmPolicy().onPathAccess(apkPath); BlockGuard.getVmPolicy().onPathAccess(outputPath); try { - mInstalld.deleteOdex(apkPath, instructionSet, outputPath); + return mInstalld.deleteOdex(apkPath, instructionSet, outputPath); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 9370b14341dc..5b2c80903ce5 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -1000,16 +1000,15 @@ public class LauncherAppsService extends SystemService { intents[0].setSourceBounds(sourceBounds); // Replace theme for splash screen - final int splashScreenThemeResId = - mShortcutServiceInternal.getShortcutStartingThemeResId(getCallingUserId(), + final String splashScreenThemeResName = + mShortcutServiceInternal.getShortcutStartingThemeResName(getCallingUserId(), callingPackage, packageName, shortcutId, targetUserId); - if (splashScreenThemeResId != 0) { + if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) { if (startActivityOptions == null) { startActivityOptions = new Bundle(); } - startActivityOptions.putInt(KEY_SPLASH_SCREEN_THEME, splashScreenThemeResId); + startActivityOptions.putString(KEY_SPLASH_SCREEN_THEME, splashScreenThemeResName); } - return startShortcutIntentsAsPublisher( intents, packageName, featureId, startActivityOptions, targetUserId); } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 131539e82af6..5fd8e3c6e302 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -31,6 +31,9 @@ import static com.android.server.pm.Installer.DEXOPT_PUBLIC; import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX; import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE; import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE; +import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES; +import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; +import static com.android.server.pm.Installer.PROFILE_ANALYSIS_OPTIMIZE; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; @@ -248,8 +251,12 @@ public class PackageDexOptimizer { || packageUseInfo.isUsedByOtherApps(path); final String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter(), isUsedByOtherApps); - final boolean profileUpdated = options.isCheckForProfileUpdates() && - isProfileUpdated(pkg, sharedGid, profileName, compilerFilter); + // If we don't have to check for profiles updates assume + // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to + // profiles. + final int profileAnalysisResult = options.isCheckForProfileUpdates() + ? analyseProfiles(pkg, sharedGid, profileName, compilerFilter) + : PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct // flags. @@ -257,7 +264,7 @@ public class PackageDexOptimizer { for (String dexCodeIsa : dexCodeInstructionSets) { int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter, - profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid, + profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid, packageStats, options.isDowngrade(), profileName, dexMetadataPath, options.getCompilationReason()); @@ -306,11 +313,11 @@ public class PackageDexOptimizer { */ @GuardedBy("mInstallLock") private int dexOptPath(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String path, - String isa, String compilerFilter, boolean profileUpdated, String classLoaderContext, + String isa, String compilerFilter, int profileAnalysisResult, String classLoaderContext, int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade, String profileName, String dexMetadataPath, int compilationReason) { int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext, - profileUpdated, downgrade); + profileAnalysisResult, downgrade); if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) { return DEX_OPT_SKIPPED; } @@ -364,7 +371,7 @@ public class PackageDexOptimizer { isa, options.getCompilerFilter(), dexUseInfo.getClassLoaderContext(), - /* newProfile= */false, + PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES, /* downgrade= */ false); if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { @@ -750,11 +757,25 @@ public class PackageDexOptimizer { * configuration (isa, compiler filter, profile). */ private int getDexoptNeeded(String path, String isa, String compilerFilter, - String classLoaderContext, boolean newProfile, boolean downgrade) { + String classLoaderContext, int profileAnalysisResult, boolean downgrade) { int dexoptNeeded; try { - dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, classLoaderContext, - newProfile, downgrade); + // A profile guided optimizations with an empty profile is essentially 'verify' and + // dex2oat already makes this transformation. However DexFile.getDexOptNeeded() cannot + // check the profiles because system server does not have access to them. + // As such, we rely on the previous profile analysis (done with dexoptanalyzer) and + // manually adjust the actual filter before checking. + // + // TODO: ideally. we'd move this check in dexoptanalyzer, but that's a large change, + // and in the interim we can still improve things here. + String actualCompilerFilter = compilerFilter; + if (compilerFilterDependsOnProfiles(compilerFilter) + && profileAnalysisResult == PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES) { + actualCompilerFilter = "verify"; + } + boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE; + dexoptNeeded = DexFile.getDexOptNeeded(path, isa, actualCompilerFilter, + classLoaderContext, newProfile, downgrade); } catch (IOException ioe) { Slog.w(TAG, "IOException reading apk: " + path, ioe); return DEX_OPT_FAILED; @@ -765,27 +786,34 @@ public class PackageDexOptimizer { return adjustDexoptNeeded(dexoptNeeded); } + /** Returns true if the compiler filter depends on profiles (e.g speed-profile). */ + private boolean compilerFilterDependsOnProfiles(String compilerFilter) { + return compilerFilter.endsWith("-profile"); + } + /** * Checks if there is an update on the profile information of the {@code pkg}. - * If the compiler filter is not profile guided the method returns false. + * If the compiler filter is not profile guided the method returns a safe default: + * PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA. * * Note that this is a "destructive" operation with side effects. Under the hood the * current profile and the reference profile will be merged and subsequent calls * may return a different result. */ - private boolean isProfileUpdated(AndroidPackage pkg, int uid, String profileName, + private int analyseProfiles(AndroidPackage pkg, int uid, String profileName, String compilerFilter) { // Check if we are allowed to merge and if the compiler filter is profile guided. if (!isProfileGuidedCompilerFilter(compilerFilter)) { - return false; + return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; } // Merge profiles. It returns whether or not there was an updated in the profile info. try { return mInstaller.mergeProfiles(uid, pkg.getPackageName(), profileName); } catch (InstallerException e) { Slog.w(TAG, "Failed to merge profiles", e); + // We don't need to optimize if we failed to merge. + return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; } - return false; } /** diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 0b63b7ddef85..d2ed08f66944 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -157,6 +157,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private volatile boolean mOkToSendBroadcasts = false; private volatile boolean mBypassNextStagedInstallerCheck = false; + private volatile boolean mBypassNextAllowedApexUpdateCheck = false; /** * File storing persisted {@link #mSessions} metadata. @@ -626,7 +627,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0; - if (params.isStaged || isApex) { + if (isApex) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGE_UPDATES) + == PackageManager.PERMISSION_DENIED + && mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) + == PackageManager.PERMISSION_DENIED) { + throw new SecurityException("Not allowed to perform APEX updates"); + } + } else if (params.isStaged) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG); } @@ -643,6 +651,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new IllegalArgumentException( "Non-staged APEX session doesn't support INSTALL_ENABLE_ROLLBACK"); } + if (isCalledBySystemOrShell(callingUid) || mBypassNextAllowedApexUpdateCheck) { + params.installFlags |= PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK; + } else { + // Only specific APEX updates (installed through ADB, or for CTS tests) can disable + // allowed APEX update check. + params.installFlags &= ~PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK; + } } if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0 @@ -667,6 +682,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } mBypassNextStagedInstallerCheck = false; + mBypassNextAllowedApexUpdateCheck = false; + if (!params.isMultiPackage) { // Only system components can circumvent runtime permissions when installing. if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0 @@ -1099,6 +1116,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mBypassNextStagedInstallerCheck = value; } + @Override + public void bypassNextAllowedApexUpdateCheck(boolean value) { + if (!isCalledBySystemOrShell(Binder.getCallingUid())) { + throw new SecurityException("Caller not allowed to bypass allowed apex update check"); + } + mBypassNextAllowedApexUpdateCheck = value; + } + /** * Set an installer to allow for the unlimited silent updates. */ @@ -1110,6 +1135,17 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSilentUpdatePolicy.setAllowUnlimitedSilentUpdates(installerPackageName); } + /** + * Set the silent updates throttle time in seconds. + */ + @Override + public void setSilentUpdatesThrottleTime(long throttleTimeInSeconds) { + if (!isCalledBySystemOrShell(Binder.getCallingUid())) { + throw new SecurityException("Caller not allowed to set silent updates throttle time"); + } + mSilentUpdatePolicy.setSilentUpdatesThrottleTime(throttleTimeInSeconds); + } + private static int getSessionCount(SparseArray<PackageInstallerSession> sessions, int installerUid) { int count = 0; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 4b0eb6546888..c33130037027 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -147,6 +147,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; +import com.android.server.SystemConfig; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.dex.DexManager; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -2238,6 +2239,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { .setAdmin(mInstallSource.installerPackageName) .write(); } + + // Check if APEX update is allowed. We do this check in handleInstall, since this is one of + // the places that: + // * Shared between staged and non-staged APEX update flows. + // * Only is called after boot completes. + // The later is important, since isApexUpdateAllowed check depends on the + // ModuleInfoProvider, which is only populated after device has booted. + if (isApexSession()) { + boolean checkApexUpdateAllowed = + (params.installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK) + == 0; + synchronized (mLock) { + if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName)) { + onSessionValidationFailure(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, + "Update of APEX package " + mPackageName + " is not allowed"); + return; + } + } + } + if (params.isStaged) { mStagingManager.commitSession(mStagedSession); // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even @@ -2776,6 +2797,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return sessionContains((s) -> !s.isApexSession()); } + private boolean isApexUpdateAllowed(String apexPackageName) { + return mPm.getModuleInfo(apexPackageName, 0) != null + || SystemConfig.getInstance().getAllowedPartnerApexes().contains(apexPackageName); + } + /** * Validate apex install. * <p> diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a8cc5fdf884d..463520fb2608 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -341,6 +341,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; +import com.android.internal.content.F2fsUtils; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.content.om.OverlayConfig; @@ -435,8 +436,10 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.charset.StandardCharsets; import java.security.DigestException; import java.security.DigestInputStream; @@ -896,6 +899,20 @@ public class PackageManagerService extends IPackageManager.Stub * Only non-null during an OTA, and even then it is nulled again once systemReady(). */ private @Nullable ArraySet<String> mExistingPackages = null; + + /** + * List of code paths that need to be released when the system becomes ready. + * <p> + * NOTE: We have to delay releasing cblocks for no other reason than we cannot + * retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. When + * we no longer need to read that setting, cblock release can occur in the + * constructor. + * + * @see Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL + * @see #systemReady() + */ + private @Nullable List<File> mReleaseOnSystemReady; + /** * Whether or not system app permissions should be promoted from install to runtime. */ @@ -1903,14 +1920,65 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * A computer provides the functional interface to the snapshot. + * A {@link Computer} provides a set of functions that can operate on live data or snapshot + * data. At this time, the {@link Computer} is implemented by the + * {@link ComputerEngine}, which is in turn extended by {@link ComputerLocked}. + * + * New functions must be added carefully. + * <ol> + * <li> New functions must be true functions with respect to data collected in a + * {@link Snapshot}. Such data may never be modified from inside a {@link Computer} + * function. + * </li> + * + * <li> A new function must be implemented in {@link ComputerEngine}. + * </li> + * + * <li> A new function must be overridden in {@link ComputerLocked} if the function + * cannot safely access live data without holding the PackageManagerService lock. The + * form of the {@link ComputerLocked} function must be a single call to the + * {@link ComputerEngine} implementation, wrapped in a <code>synchronized</code> + * block. Functions in {@link ComputerLocked} should never include any other code. + * </li> + * + * Care must be taken when deciding if a function should be overridden in + * {@link ComputerLocked}. The complex lock relationships of PackageManagerService + * and other managers (like PermissionManager) mean deadlock is possible. On the + * other hand, not overriding in {@link ComputerLocked} may leave a function walking + * unstable data. + * + * To coax developers to consider such issues carefully, all methods in + * {@link Computer} must be annotated with <code>@LiveImplementation(override = + * MANDATORY)</code> or <code>LiveImplementation(locked = NOT_ALLOWED)</code>. A unit + * test verifies the annotation and that the annotation corresponds to the code in + * {@link ComputerEngine} and {@link ComputerLocked}. */ - private interface Computer { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected interface Computer { + + /** + * Every method must be annotated. + */ + @Target({ ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + public @interface LiveImplementation { + // A Computer method must be annotated with one of the following values: + // MANDATORY - the method must be overridden in ComputerEngineLive. The + // format of the override is a call to the super method, wrapped in a + // synchronization block. + // NOT_ALLOWED - the method may not appear in the live computer. It must + // be final in the ComputerEngine. + int MANDATORY = 1; + int NOT_ALLOWED = 2; + int override() default MANDATORY; + String rationale() default ""; + } /** * Administrative statistics: record that the snapshot has been used. Every call * to use() increments the usage counter. */ + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) default void use() { } @@ -1918,95 +1986,154 @@ public class PackageManagerService extends IPackageManager.Stub * Fetch the snapshot usage counter. * @return The number of times this snapshot was used. */ + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) default int getUsed() { return 0; } + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps); + @LiveImplementation(override = LiveImplementation.MANDATORY) @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits, String pkgName, String instantAppPkgName); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ActivityInfo getActivityInfo(ComponentName component, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ActivityInfo getActivityInfoInternal(ComponentName component, int flags, int filterCallingUid, int userId); + @LiveImplementation(override = LiveImplementation.MANDATORY) AndroidPackage getPackage(String packageName); + @LiveImplementation(override = LiveImplementation.MANDATORY) AndroidPackage getPackage(int uid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags, int filterCallingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ApplicationInfo getApplicationInfo(String packageName, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ApplicationInfo getApplicationInfoInternal(String packageName, int flags, int filterCallingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ComponentName getDefaultHomeActivity(int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType, int flags, int sourceUserId, int parentUserId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) Intent getHomeIntent(); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) PackageInfo getPackageInfo(String packageName, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags, int filterCallingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) PackageSetting getPackageSetting(String packageName); + @LiveImplementation(override = LiveImplementation.MANDATORY) PackageSetting getPackageSettingInternal(String packageName, int callingUid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter, int sourceUserId, int targetUserId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ServiceInfo getServiceInfo(ComponentName component, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version); + @LiveImplementation(override = LiveImplementation.MANDATORY) String getInstantAppPackageName(int callingUid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) String resolveExternalPackageNameLPr(AndroidPackage pkg); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) String resolveInternalPackageNameLPr(String packageName, long versionCode); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) String[] getPackagesForUid(int uid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) UserInfo getProfileParent(int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean canViewInstantApps(int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId, int flags); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isCallerSameApp(String packageName, int uid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isComponentVisibleToInstantApp(@Nullable ComponentName component); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isComponentVisibleToInstantApp(@Nullable ComponentName component, @ComponentType int type); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId, String resolvedType, int flags); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isInstantApp(String packageName, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid, @Nullable ComponentName component, @ComponentType int componentType, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int checkUidPermission(String permName, int uid); + @LiveImplementation(override = LiveImplementation.MANDATORY) int getPackageUidInternal(String packageName, int flags, int userId, int callingUid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForApplication(int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForComponent(int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForPackage(int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, boolean requirePermissionWhenSameUser, String message); + @LiveImplementation(override = LiveImplementation.MANDATORY) SigningDetails getSigningDetails(@NonNull String packageName); + @LiveImplementation(override = LiveImplementation.MANDATORY) SigningDetails getSigningDetails(int uid); + @LiveImplementation(override = LiveImplementation.MANDATORY) boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.MANDATORY) boolean filterAppAccess(String packageName, int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.MANDATORY) void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState); } @@ -2015,7 +2142,8 @@ public class PackageManagerService extends IPackageManager.Stub * is entirely self-contained - it has no implicit access to * PackageManagerService. */ - private static class ComputerEngine implements Computer { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected static class ComputerEngine implements Computer { // The administrative use counter. private int mUsed = 0; @@ -2061,7 +2189,7 @@ public class PackageManagerService extends IPackageManager.Stub // PackageManagerService attributes that are primitives are referenced through the // pms object directly. Primitives are the only attributes so referenced. protected final PackageManagerService mService; - protected boolean safeMode() { + private boolean safeMode() { return mService.mSafeMode; } protected ComponentName resolveComponentName() { @@ -2115,18 +2243,18 @@ public class PackageManagerService extends IPackageManager.Stub /** * Record that the snapshot was used. */ - public void use() { + public final void use() { mUsed++; } /** * Return the usage counter. */ - public int getUsed() { + public final int getUsed() { return mUsed; } - public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, + public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { @@ -2225,14 +2353,14 @@ public class PackageManagerService extends IPackageManager.Stub resolveForStart, userId, intent); } - public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, + public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { return queryIntentActivitiesInternal( intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(), userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); } - public @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, + public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!mUserManager.exists(userId)) return Collections.emptyList(); @@ -2456,7 +2584,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { + public final ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId); } @@ -2466,7 +2594,7 @@ public class PackageManagerService extends IPackageManager.Stub * to clearing. Because it can only be provided by trusted code, its value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ - public ActivityInfo getActivityInfoInternal(ComponentName component, int flags, + public final ActivityInfo getActivityInfoInternal(ComponentName component, int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId); @@ -2520,8 +2648,8 @@ public class PackageManagerService extends IPackageManager.Stub return pkg; } - public ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags, - int filterCallingUid, int userId) { + public final ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, + int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { @@ -2548,7 +2676,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { + public final ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId); } @@ -2558,7 +2686,7 @@ public class PackageManagerService extends IPackageManager.Stub * to clearing. Because it can only be provided by trusted code, its value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ - public ApplicationInfo getApplicationInfoInternal(String packageName, int flags, + public final ApplicationInfo getApplicationInfoInternal(String packageName, int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForApplication(flags, userId); @@ -2750,7 +2878,7 @@ public class PackageManagerService extends IPackageManager.Stub * Report the 'Home' activity which is currently set as "always use this one". If non is set * then reports the most likely home activity or null if there are more than one. */ - public ComponentName getDefaultHomeActivity(int userId) { + public final ComponentName getDefaultHomeActivity(int userId) { List<ResolveInfo> allHomeCandidates = new ArrayList<>(); ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId); if (cn != null) { @@ -2778,7 +2906,7 @@ public class PackageManagerService extends IPackageManager.Stub return lastComponent; } - public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, + public final ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId) { Intent intent = getHomeIntent(); List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null, @@ -2807,7 +2935,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, + public final CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType, int flags, int sourceUserId, int parentUserId) { if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, sourceUserId)) { @@ -2853,15 +2981,15 @@ public class PackageManagerService extends IPackageManager.Stub return result; } - public Intent getHomeIntent() { + public final Intent getHomeIntent() { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); intent.addCategory(Intent.CATEGORY_DEFAULT); return intent; } - public List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, - String resolvedType, int userId) { + public final List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters( + Intent intent, String resolvedType, int userId) { CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); @@ -2880,7 +3008,8 @@ public class PackageManagerService extends IPackageManager.Stub * @param intent * @return A filtered list of resolved activities. */ - public List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos, + public final List<ResolveInfo> applyPostResolutionFilter( + @NonNull List<ResolveInfo> resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent) { final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId); @@ -3026,7 +3155,7 @@ public class PackageManagerService extends IPackageManager.Stub return resolveInfos; } - public List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, + private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; @@ -3173,7 +3302,7 @@ public class PackageManagerService extends IPackageManager.Stub return result; } - public PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { + public final PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { if (!mUserManager.exists(userId)) return null; if (ps == null) { return null; @@ -3243,7 +3372,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - public PackageInfo getPackageInfo(String packageName, int flags, int userId) { + public final PackageInfo getPackageInfo(String packageName, int flags, int userId) { return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST, flags, Binder.getCallingUid(), userId); } @@ -3254,7 +3383,7 @@ public class PackageManagerService extends IPackageManager.Stub * to clearing. Because it can only be provided by trusted code, its value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ - public PackageInfo getPackageInfoInternal(String packageName, long versionCode, + public final PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForPackage(flags, userId); @@ -3325,7 +3454,7 @@ public class PackageManagerService extends IPackageManager.Stub } @Nullable - public PackageSetting getPackageSetting(String packageName) { + public final PackageSetting getPackageSetting(String packageName) { return getPackageSettingInternal(packageName, Binder.getCallingUid()); } @@ -3335,7 +3464,7 @@ public class PackageManagerService extends IPackageManager.Stub return mSettings.getPackageLPr(packageName); } - public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) { + public final ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); @@ -3474,7 +3603,7 @@ public class PackageManagerService extends IPackageManager.Stub return new CrossProfileDomainInfo(forwardingInfo, highestApprovalLevel); } - public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter, + public final ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); final long ident = Binder.clearCallingIdentity(); @@ -3586,7 +3715,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { + public final ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId); @@ -3620,7 +3749,7 @@ public class PackageManagerService extends IPackageManager.Stub } @Nullable - public SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { + public final SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @@ -3656,7 +3785,7 @@ public class PackageManagerService extends IPackageManager.Stub return ownerUid; } - public String resolveExternalPackageNameLPr(AndroidPackage pkg) { + public final String resolveExternalPackageNameLPr(AndroidPackage pkg) { if (pkg.getStaticSharedLibName() != null) { return pkg.getManifestPackageName(); } @@ -3730,7 +3859,7 @@ public class PackageManagerService extends IPackageManager.Stub return packageName; } - public String resolveInternalPackageNameLPr(String packageName, long versionCode) { + public final String resolveInternalPackageNameLPr(String packageName, long versionCode) { final int callingUid = Binder.getCallingUid(); return resolveInternalPackageNameInternalLocked(packageName, versionCode, callingUid); @@ -3751,7 +3880,7 @@ public class PackageManagerService extends IPackageManager.Stub * calls to invalidateGetPackagesForUidCache() to locate the points at * which the cache is invalidated. */ - public String[] getPackagesForUid(int uid) { + public final String[] getPackagesForUid(int uid) { return getPackagesForUidInternal(uid, Binder.getCallingUid()); } @@ -3792,7 +3921,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public UserInfo getProfileParent(int userId) { + public final UserInfo getProfileParent(int userId) { final long identity = Binder.clearCallingIdentity(); try { return mUserManager.getProfileParent(userId); @@ -3822,7 +3951,7 @@ public class PackageManagerService extends IPackageManager.Stub * <li>The calling application is the default app prediction service.</li> * </ol> */ - public boolean canViewInstantApps(int callingUid, int userId) { + public final boolean canViewInstantApps(int callingUid, int userId) { if (callingUid < Process.FIRST_APPLICATION_UID) { return true; } @@ -3846,8 +3975,8 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - public boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId, - int flags) { + public final boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, + int userId, int flags) { // Callers can access only the libs they depend on, otherwise they need to explicitly // ask for the shared libraries given the caller is allowed to access all static libs. if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) { @@ -3930,13 +4059,13 @@ public class PackageManagerService extends IPackageManager.Stub == PackageManager.PERMISSION_GRANTED; } - public boolean isCallerSameApp(String packageName, int uid) { + public final boolean isCallerSameApp(String packageName, int uid) { AndroidPackage pkg = mPackages.get(packageName); return pkg != null && UserHandle.getAppId(uid) == pkg.getUid(); } - public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) { + public final boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) { if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) { return true; } @@ -3949,7 +4078,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - public boolean isComponentVisibleToInstantApp( + public final boolean isComponentVisibleToInstantApp( @Nullable ComponentName component, @ComponentType int type) { if (type == TYPE_ACTIVITY) { final ParsedActivity activity = mComponentResolver.getActivity(component); @@ -3997,13 +4126,13 @@ public class PackageManagerService extends IPackageManager.Stub * @return {@code true} if the intent is a camera intent and the persistent preferred * activity was not set by the DPC. */ - public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId, - String resolvedType, int flags) { + public final boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, + int userId, String resolvedType, int flags) { return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm( intent, userId, resolvedType, flags); } - public boolean isInstantApp(String packageName, int userId) { + public final boolean isInstantApp(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); @@ -4011,7 +4140,7 @@ public class PackageManagerService extends IPackageManager.Stub return isInstantAppInternal(packageName, userId, callingUid); } - public boolean isInstantAppInternal(String packageName, @UserIdInt int userId, + public final boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid) { if (HIDE_EPHEMERAL_APIS) { return false; @@ -4145,7 +4274,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - public boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { + public final boolean isSameProfileGroup(@UserIdInt int callerUserId, + @UserIdInt int userId) { final long identity = Binder.clearCallingIdentity(); try { return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId); @@ -4172,7 +4302,8 @@ public class PackageManagerService extends IPackageManager.Stub * * @see #canViewInstantApps(int, int) */ - public boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid, + public final boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, + int callingUid, @Nullable ComponentName component, @ComponentType int componentType, int userId) { // if we're in an isolated process, get the real calling UID if (Process.isIsolated(callingUid)) { @@ -4232,7 +4363,7 @@ public class PackageManagerService extends IPackageManager.Stub /** * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int) */ - public boolean shouldFilterApplicationLocked( + public final boolean shouldFilterApplicationLocked( @Nullable PackageSetting ps, int callingUid, int userId) { return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId); } @@ -4240,8 +4371,8 @@ public class PackageManagerService extends IPackageManager.Stub /** * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int) */ - public boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid, - int userId) { + public final boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, + int callingUid, int userId) { boolean filterApp = true; for (int index = sus.packages.size() - 1; index >= 0 && filterApp; index--) { filterApp &= shouldFilterApplicationLocked(sus.packages.valueAt(index), @@ -4265,7 +4396,7 @@ public class PackageManagerService extends IPackageManager.Stub } // NOTE: Can't remove without a major refactor. Keep around for now. - public int checkUidPermission(String permName, int uid) { + public final int checkUidPermission(String permName, int uid) { return mPermissionManager.checkUidPermission(uid, permName); } @@ -4315,21 +4446,21 @@ public class PackageManagerService extends IPackageManager.Stub /** * Update given flags when being used to request {@link ApplicationInfo}. */ - public int updateFlagsForApplication(int flags, int userId) { + public final int updateFlagsForApplication(int flags, int userId) { return updateFlagsForPackage(flags, userId); } /** * Update given flags when being used to request {@link ComponentInfo}. */ - public int updateFlagsForComponent(int flags, int userId) { + public final int updateFlagsForComponent(int flags, int userId) { return updateFlags(flags, userId); } /** * Update given flags when being used to request {@link PackageInfo}. */ - public int updateFlagsForPackage(int flags, int userId) { + public final int updateFlagsForPackage(int flags, int userId) { final boolean isCallerSystemUser = UserHandle.getCallingUserId() == UserHandle.USER_SYSTEM; if ((flags & PackageManager.MATCH_ANY_USER) != 0) { @@ -4365,14 +4496,14 @@ public class PackageManagerService extends IPackageManager.Stub * action and a {@code android.intent.category.BROWSABLE} category</li> * </ul> */ - public int updateFlagsForResolve(int flags, int userId, int callingUid, + public final int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) { return updateFlagsForResolve(flags, userId, callingUid, wantInstantApps, false /*onlyExposedExplicitly*/, isImplicitImageCaptureIntentAndNotSetByDpc); } - public int updateFlagsForResolve(int flags, int userId, int callingUid, + public final int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc) { // Safe mode means we shouldn't match any third-party components @@ -4414,7 +4545,7 @@ public class PackageManagerService extends IPackageManager.Stub * @param checkShell whether to prevent shell from access if there's a debugging restriction * @param message the message to log on security exception */ - public void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, + public final void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message) { if (userId < 0) { throw new IllegalArgumentException("Invalid userId " + userId); @@ -4452,7 +4583,7 @@ public class PackageManagerService extends IPackageManager.Stub * @param checkShell whether to prevent shell from access if there's a debugging restriction * @param message the message to log on security exception */ - public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, + public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message) { enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false, message); @@ -4469,7 +4600,7 @@ public class PackageManagerService extends IPackageManager.Stub * reference the same user. * @param message the message to log on security exception */ - public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, + public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, boolean requirePermissionWhenSameUser, String message) { if (userId < 0) { @@ -4721,31 +4852,14 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * The live computer differs from the ComputerEngine in the methods that fetch data - * from PackageManagerService. - **/ - private static class ComputerEngineLive extends ComputerEngine { - ComputerEngineLive(Snapshot args) { - super(args); - } - protected ComponentName resolveComponentName() { - return mService.mResolveComponentName; - } - protected ActivityInfo instantAppInstallerActivity() { - return mService.mInstantAppInstallerActivity; - } - protected ApplicationInfo androidApplication() { - return mService.mAndroidApplication; - } - } - - /** - * This subclass is the external interface to the live computer. For each - * interface, it takes the PM lock and then delegates to the live - * computer engine. This is required because there are no locks taken in - * the engine itself. + * This subclass is the external interface to the live computer. Some internal helper + * methods are overridden to fetch live data instead of snapshot data. For each + * Computer interface that is overridden in this class, the override takes the PM lock + * and then delegates to the live computer engine. This is required because there are + * no locks taken in the engine itself. */ - private static class ComputerLocked extends ComputerEngineLive { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected static class ComputerLocked extends ComputerEngine { private final Object mLock; ComputerLocked(Snapshot args) { @@ -4753,18 +4867,17 @@ public class PackageManagerService extends IPackageManager.Stub mLock = mService.mLock; } - /** - * Explicilty snapshot {@link Settings#mPackages} for cases where the caller must not lock - * in order to get package data. It is expected that the caller locks itself to be able - * to block on changes to the package data and bring itself up to date once the change - * propagates to it. Use with heavy caution. - * @return - */ - private Map<String, PackageSetting> snapshotPackageSettings() { - return mSettings.snapshot().mPackages; + protected final ComponentName resolveComponentName() { + return mService.mResolveComponentName; + } + protected final ActivityInfo instantAppInstallerActivity() { + return mService.mInstantAppInstallerActivity; + } + protected final ApplicationInfo androidApplication() { + return mService.mAndroidApplication; } - public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent, + public final @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent, String resolvedType, int flags, int userId, int callingUid, String instantAppPkgName) { synchronized (mLock) { @@ -4772,8 +4885,8 @@ public class PackageManagerService extends IPackageManager.Stub callingUid, instantAppPkgName); } } - public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent, - String resolvedType, int flags, int filterCallingUid, int userId, + public final @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody( + Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits, String pkgName, String instantAppPkgName) { synchronized (mLock) { @@ -4782,31 +4895,31 @@ public class PackageManagerService extends IPackageManager.Stub instantAppPkgName); } } - public ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags, + public final ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags, int filterCallingUid, int userId) { synchronized (mLock) { return super.getActivityInfoInternalBody(component, flags, filterCallingUid, userId); } } - public AndroidPackage getPackage(String packageName) { + public final AndroidPackage getPackage(String packageName) { synchronized (mLock) { return super.getPackage(packageName); } } - public AndroidPackage getPackage(int uid) { + public final AndroidPackage getPackage(int uid) { synchronized (mLock) { return super.getPackage(uid); } } - public ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags, + public final ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags, int filterCallingUid, int userId) { synchronized (mLock) { return super.getApplicationInfoInternalBody(packageName, flags, filterCallingUid, userId); } } - public ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody( + public final ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody( Intent intent, int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) { synchronized (mLock) { @@ -4814,49 +4927,49 @@ public class PackageManagerService extends IPackageManager.Stub matchFlags, candidates, xpDomainInfo, userId, debug); } } - public PackageInfo getPackageInfoInternalBody(String packageName, long versionCode, + public final PackageInfo getPackageInfoInternalBody(String packageName, long versionCode, int flags, int filterCallingUid, int userId) { synchronized (mLock) { return super.getPackageInfoInternalBody(packageName, versionCode, flags, filterCallingUid, userId); } } - public PackageSetting getPackageSettingInternal(String packageName, int callingUid) { + public final PackageSetting getPackageSettingInternal(String packageName, int callingUid) { synchronized (mLock) { return super.getPackageSettingInternal(packageName, callingUid); } } - public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId, + public final ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId, int callingUid) { synchronized (mLock) { return super.getInstalledPackagesBody(flags, userId, callingUid); } } - public ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId, + public final ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId, int callingUid) { synchronized (mLock) { return super.getServiceInfoBody(component, flags, userId, callingUid); } } - public String getInstantAppPackageName(int callingUid) { + public final String getInstantAppPackageName(int callingUid) { synchronized (mLock) { return super.getInstantAppPackageName(callingUid); } } - public String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId, + public final String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId, boolean isCallerInstantApp) { synchronized (mLock) { return super.getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp); } } - public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, + public final boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, int callingUid) { synchronized (mLock) { return super.isInstantAppInternalBody(packageName, userId, callingUid); } } - public boolean isInstantAppResolutionAllowedBody(Intent intent, + public final boolean isInstantAppResolutionAllowedBody(Intent intent, List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck, int flags) { synchronized (mLock) { @@ -4864,33 +4977,33 @@ public class PackageManagerService extends IPackageManager.Stub skipPackageCheck, flags); } } - public int getPackageUidInternal(String packageName, int flags, int userId, + public final int getPackageUidInternal(String packageName, int flags, int userId, int callingUid) { synchronized (mLock) { return super.getPackageUidInternal(packageName, flags, userId, callingUid); } } - public SigningDetails getSigningDetails(@NonNull String packageName) { + public final SigningDetails getSigningDetails(@NonNull String packageName) { synchronized (mLock) { return super.getSigningDetails(packageName); } } - public SigningDetails getSigningDetails(int uid) { + public final SigningDetails getSigningDetails(int uid) { synchronized (mLock) { return super.getSigningDetails(uid); } } - public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { + public final boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { synchronized (mLock) { return super.filterAppAccess(pkg, callingUid, userId); } } - public boolean filterAppAccess(String packageName, int callingUid, int userId) { + public final boolean filterAppAccess(String packageName, int callingUid, int userId) { synchronized (mLock) { return super.filterAppAccess(packageName, callingUid, userId); } } - public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) { + public final void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) { synchronized (mLock) { super.dump(type, fd, pw, dumpState); } @@ -7966,6 +8079,21 @@ public class PackageManagerService extends IPackageManager.Stub IoUtils.closeQuietly(handle); } } + if (ret == PackageManager.INSTALL_SUCCEEDED) { + // NOTE: During boot, we have to delay releasing cblocks for no other reason than + // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. + // When we no longer need to read that setting, cblock release can occur always + // occur here directly + if (!mSystemReady) { + if (mReleaseOnSystemReady == null) { + mReleaseOnSystemReady = new ArrayList<>(); + } + mReleaseOnSystemReady.add(dstCodePath); + } else { + final ContentResolver resolver = mContext.getContentResolver(); + F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath); + } + } if (ret != PackageManager.INSTALL_SUCCEEDED) { if (!dstCodePath.exists()) { return null; @@ -11524,8 +11652,31 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } + final List<String> names = new ArrayList<>(); + final List<ProviderInfo> infos = new ArrayList<>(); + final int callingUserId = UserHandle.getCallingUserId(); mComponentResolver.querySyncProviders( - outNames, outInfo, mSafeMode, UserHandle.getCallingUserId()); + names, infos, mSafeMode, callingUserId); + synchronized (mLock) { + for (int i = infos.size() - 1; i >= 0; i--) { + final ProviderInfo providerInfo = infos.get(i); + final PackageSetting ps = mSettings.getPackageLPr(providerInfo.packageName); + final ComponentName component = + new ComponentName(providerInfo.packageName, providerInfo.name); + if (!shouldFilterApplicationLocked(ps, Binder.getCallingUid(), component, + TYPE_PROVIDER, callingUserId)) { + continue; + } + infos.remove(i); + names.remove(i); + } + } + if (!names.isEmpty()) { + outNames.addAll(names); + } + if (!infos.isEmpty()) { + outInfo.addAll(infos); + } } @Override @@ -12647,21 +12798,6 @@ public class PackageManagerService extends IPackageManager.Stub return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); } - /** - * Ask the package manager to compile layouts in the given package. - */ - @Override - public boolean compileLayouts(String packageName) { - AndroidPackage pkg; - synchronized (mLock) { - pkg = mPackages.get(packageName); - if (pkg == null) { - return false; - } - } - return mViewCompiler.compileLayouts(pkg); - } - /*package*/ boolean performDexOpt(DexoptOptions options) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; @@ -12958,6 +13094,15 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void dumpProfiles(String packageName) { + /* Only the shell, root, or the app user should be able to dump profiles. */ + final int callingUid = Binder.getCallingUid(); + final String[] callerPackageNames = getPackagesForUid(callingUid); + if (callingUid != Process.SHELL_UID + && callingUid != Process.ROOT_UID + && !ArrayUtils.contains(callerPackageNames, packageName)) { + throw new SecurityException("dumpProfiles"); + } + AndroidPackage pkg; synchronized (mLock) { pkg = mPackages.get(packageName); @@ -12965,13 +13110,6 @@ public class PackageManagerService extends IPackageManager.Stub throw new IllegalArgumentException("Unknown package: " + packageName); } } - /* Only the shell, root, or the app user should be able to dump profiles. */ - int callingUid = Binder.getCallingUid(); - if (callingUid != Process.SHELL_UID && - callingUid != Process.ROOT_UID && - callingUid != pkg.getUid()) { - throw new SecurityException("dumpProfiles"); - } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); @@ -17097,10 +17235,7 @@ public class PackageManagerService extends IPackageManager.Stub callerPackageName); synchronized (mLock) { PackageSetting ps = mSettings.getPackageLPr(packageName); - if (ps == null) { - throw new IllegalArgumentException("Unknown target package " + packageName); - } - if (shouldFilterApplicationLocked( + if (ps == null || shouldFilterApplicationLocked( ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { throw new IllegalArgumentException("Unknown target package " + packageName); } @@ -17853,6 +17988,10 @@ public class PackageManagerService extends IPackageManager.Stub if (mRet == PackageManager.INSTALL_SUCCEEDED) { mRet = args.copyApk(); } + if (mRet == PackageManager.INSTALL_SUCCEEDED) { + F2fsUtils.releaseCompressedBlocks( + mContext.getContentResolver(), new File(args.getCodePath())); + } if (mParentInstallParams != null) { mParentInstallParams.tryProcessInstallRequest(args, mRet); } else { @@ -17860,7 +17999,6 @@ public class PackageManagerService extends IPackageManager.Stub processInstallRequestsAsync( res.returnCode == PackageManager.INSTALL_SUCCEEDED, Collections.singletonList(new InstallRequest(args, res))); - } } } @@ -23158,16 +23296,17 @@ public class PackageManagerService extends IPackageManager.Stub if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) { return; } + final String[] callerPackageNames = getPackagesForUid(callingUid); + if (!ArrayUtils.contains(callerPackageNames, pkg)) { + throw new SecurityException("Calling uid " + callingUid + + " does not own package " + pkg); + } final int callingUserId = UserHandle.getUserId(callingUid); PackageInfo pi = getPackageInfo(pkg, 0, callingUserId); if (pi == null) { throw new IllegalArgumentException("Unknown package " + pkg + " on user " + callingUserId); } - if (!UserHandle.isSameApp(pi.applicationInfo.uid, callingUid)) { - throw new SecurityException("Calling uid " + callingUid - + " does not own package " + pkg); - } } @Override @@ -23983,6 +24122,13 @@ public class PackageManagerService extends IPackageManager.Stub final int permission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + if (!allowedByPermission + && !ArrayUtils.contains(getPackagesForUid(callingUid), packageName)) { + throw new SecurityException( + "Permission Denial: attempt to change stopped state from pid=" + + Binder.getCallingPid() + + ", uid=" + callingUid + ", package=" + packageName); + } enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "stop package"); boolean shouldUnhibernate = false; @@ -23993,8 +24139,7 @@ public class PackageManagerService extends IPackageManager.Stub shouldUnhibernate = true; } if (!shouldFilterApplicationLocked(ps, callingUid, userId) - && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, - allowedByPermission, callingUid, userId)) { + && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, userId)) { scheduleWritePackageRestrictionsLocked(userId); } } @@ -24235,8 +24380,15 @@ public class PackageManagerService extends IPackageManager.Stub public void systemReady() { enforceSystemOrRoot("Only the system can claim the system is ready"); - mSystemReady = true; final ContentResolver resolver = mContext.getContentResolver(); + if (mReleaseOnSystemReady != null) { + for (int i = mReleaseOnSystemReady.size() - 1; i >= 0; --i) { + final File dstCodePath = mReleaseOnSystemReady.get(i); + F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath); + } + mReleaseOnSystemReady = null; + } + mSystemReady = true; ContentObserver co = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { @@ -26326,7 +26478,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); scheduleWritePackageListLocked(userId); - mAppsFilter.onUsersChanged(); + mAppsFilter.onUserCreated(userId); } } @@ -26421,16 +26573,12 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized(mLock) { final AndroidPackage pkg = mPackages.get(packageName); - if (pkg == null) { + if (pkg == null + || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()), + Binder.getCallingUid(), UserHandle.getCallingUserId())) { Slog.w(TAG, "KeySet requested for unknown package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } - final PackageSetting ps = getPackageSetting(pkg.getPackageName()); - if (shouldFilterApplicationLocked( - ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { - Slog.w(TAG, "KeySet requested for filtered package: " + packageName); - throw new IllegalArgumentException("Unknown package: " + packageName); - } final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias)); } @@ -26445,14 +26593,10 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final AndroidPackage pkg = mPackages.get(packageName); - if (pkg == null) { - Slog.w(TAG, "KeySet requested for unknown package: " + packageName); - throw new IllegalArgumentException("Unknown package: " + packageName); - } - final PackageSetting ps = getPackageSetting(pkg.getPackageName()); - if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { - // filter and pretend the package doesn't exist - Slog.w(TAG, "KeySet requested for filtered package: " + packageName + if (pkg == null + || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()), + callingUid, callingUserId)) { + Slog.w(TAG, "KeySet requested for unknown package: " + packageName + ", uid:" + callingUid); throw new IllegalArgumentException("Unknown package: " + packageName); } @@ -27892,7 +28036,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getMimeGroup(String packageName, String mimeGroup) { - return PackageManagerService.this.getMimeGroup(packageName, mimeGroup); + return PackageManagerService.this.getMimeGroupInternal(packageName, mimeGroup); } @Override @@ -28391,14 +28535,14 @@ public class PackageManagerService extends IPackageManager.Stub } } - void deleteOatArtifactsOfPackage(String packageName) { + long deleteOatArtifactsOfPackage(String packageName) { final AndroidPackage pkg; final PackageSetting pkgSetting; synchronized (mLock) { pkg = mPackages.get(packageName); pkgSetting = mSettings.getPackageLPr(packageName); } - mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting)); + return mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting)); } Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) { @@ -28518,9 +28662,11 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) { - boolean changed = mSettings.getPackageLPr(packageName) - .setMimeGroup(mimeGroup, mimeTypes); - + enforceOwnerRights(packageName, Binder.getCallingUid()); + final boolean changed; + synchronized (mLock) { + changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup, mimeTypes); + } if (changed) { applyMimeGroupChanges(packageName, mimeGroup); } @@ -28528,7 +28674,14 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getMimeGroup(String packageName, String mimeGroup) { - return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup); + enforceOwnerRights(packageName, Binder.getCallingUid()); + return getMimeGroupInternal(packageName, mimeGroup); + } + + private List<String> getMimeGroupInternal(String packageName, String mimeGroup) { + synchronized (mLock) { + return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup); + } } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index f9c63a948a86..1aa80a953d6c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -307,8 +307,10 @@ class PackageManagerShellCommand extends ShellCommand { return runLogVisibility(); case "bypass-staged-installer-check": return runBypassStagedInstallerCheck(); - case "allow-unlimited-silent-updates": - return runAllowUnlimitedSilentUpdates(); + case "bypass-allowed-apex-update-check": + return runBypassAllowedApexUpdateCheck(); + case "set-silent-updates-policy": + return runSetSilentUpdatesPolicy(); default: { Boolean domainVerificationResult = mDomainVerificationShell.runCommand(this, cmd); @@ -424,6 +426,20 @@ class PackageManagerShellCommand extends ShellCommand { } } + private int runBypassAllowedApexUpdateCheck() { + final PrintWriter pw = getOutPrintWriter(); + try { + mInterface.getPackageInstaller() + .bypassNextAllowedApexUpdateCheck(Boolean.parseBoolean(getNextArg())); + return 0; + } catch (RemoteException e) { + pw.println("Failure [" + + e.getClass().getName() + " - " + + e.getMessage() + "]"); + return -1; + } + } + private int uninstallSystemUpdates(String packageName) { final PrintWriter pw = getOutPrintWriter(); boolean failedUninstalls = false; @@ -3041,12 +3057,20 @@ class PackageManagerShellCommand extends ShellCommand { } } - private int runAllowUnlimitedSilentUpdates() { + private int runSetSilentUpdatesPolicy() { final PrintWriter pw = getOutPrintWriter(); String opt; + String installerPackageName = null; + Long throttleTimeInSeconds = null; boolean reset = false; while ((opt = getNextOption()) != null) { switch (opt) { + case "--allow-unlimited-silent-updates": + installerPackageName = getNextArgRequired(); + break; + case "--throttle-time": + throttleTimeInSeconds = Long.parseLong(getNextArgRequired()); + break; case "--reset": reset = true; break; @@ -3055,10 +3079,24 @@ class PackageManagerShellCommand extends ShellCommand { return -1; } } + if (throttleTimeInSeconds != null && throttleTimeInSeconds < 0) { + pw.println("Error: Invalid value for \"--throttle-time\":" + throttleTimeInSeconds); + return -1; + } - final String installerPackageName = reset ? null : getNextArgRequired(); try { - mInterface.getPackageInstaller().setAllowUnlimitedSilentUpdates(installerPackageName); + final IPackageInstaller installer = mInterface.getPackageInstaller(); + if (reset) { + installer.setAllowUnlimitedSilentUpdates(null /* installerPackageName */); + installer.setSilentUpdatesThrottleTime(-1 /* restore to the default */); + } else { + if (installerPackageName != null) { + installer.setAllowUnlimitedSilentUpdates(installerPackageName); + } + if (throttleTimeInSeconds != null) { + installer.setSilentUpdatesThrottleTime(throttleTimeInSeconds); + } + } } catch (RemoteException e) { pw.println("Failure [" + e.getClass().getName() + " - " @@ -3889,11 +3927,14 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" --enable: turn on debug logging (default)"); pw.println(" --disable: turn off debug logging"); pw.println(""); - pw.println(" allow-unlimited-silent-updates (--reset | <INSTALLER>)"); - pw.println(" Allows unlimited silent updated installation requests from the installer"); - pw.println(" without the throttle time."); - pw.println(" --reset: clear the allowed installer and tracks of silent updates in"); - pw.println(" the system."); + pw.println(" set-silent-updates-policy [--allow-unlimited-silent-updates <INSTALLER>]"); + pw.println(" [--throttle-time <SECONDS>] [--reset]"); + pw.println(" Sets the policies of the silent updates."); + pw.println(" --allow-unlimited-silent-updates: allows unlimited silent updated"); + pw.println(" installation requests from the installer without the throttle time."); + pw.println(" --throttle-time: update the silent updates throttle time in seconds."); + pw.println(" --reset: restore the installer and throttle time to the default, and"); + pw.println(" clear tracks of silent updates in the system."); pw.println(""); mDomainVerificationShell.printHelp(pw); pw.println(""); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 7aa1c3ab8154..26aebbc1ea93 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4188,18 +4188,11 @@ public final class Settings implements Watchable, Snappable { } boolean setPackageStoppedStateLPw(PackageManagerService pm, String packageName, - boolean stopped, boolean allowedByPermission, int uid, int userId) { - int appId = UserHandle.getAppId(uid); + boolean stopped, int userId) { final PackageSetting pkgSetting = mPackages.get(packageName); if (pkgSetting == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } - if (!allowedByPermission && (appId != pkgSetting.appId)) { - throw new SecurityException( - "Permission Denial: attempt to change stopped state from pid=" - + Binder.getCallingPid() - + ", uid=" + uid + ", package uid=" + pkgSetting.appId); - } if (DEBUG_STOPPED) { if (stopped) { RuntimeException e = new RuntimeException("here"); diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 1bd9e5eedb84..b4bd086af272 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -139,7 +139,7 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String ATTR_BITMAP_PATH = "bitmap-path"; private static final String ATTR_ICON_URI = "icon-uri"; private static final String ATTR_LOCUS_ID = "locus-id"; - private static final String ATTR_SPLASH_SCREEN_THEME_ID = "splash-screen-theme-id"; + private static final String ATTR_SPLASH_SCREEN_THEME_NAME = "splash-screen-theme-name"; private static final String ATTR_PERSON_NAME = "name"; private static final String ATTR_PERSON_URI = "uri"; @@ -1799,7 +1799,7 @@ class ShortcutPackage extends ShortcutPackageItem { ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle()); ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId()); ShortcutService.writeAttr(out, ATTR_TITLE_RES_NAME, si.getTitleResName()); - ShortcutService.writeAttr(out, ATTR_SPLASH_SCREEN_THEME_ID, si.getStartingThemeResId()); + ShortcutService.writeAttr(out, ATTR_SPLASH_SCREEN_THEME_NAME, si.getStartingThemeResName()); ShortcutService.writeAttr(out, ATTR_TEXT, si.getText()); ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId()); ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName()); @@ -2010,7 +2010,7 @@ class ShortcutPackage extends ShortcutPackageItem { String bitmapPath; String iconUri; final String locusIdString; - int splashScreenThemeResId; + String splashScreenThemeResName; int backupVersionCode; ArraySet<String> categories = null; ArrayList<Person> persons = new ArrayList<>(); @@ -2021,8 +2021,8 @@ class ShortcutPackage extends ShortcutPackageItem { title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE); titleResId = ShortcutService.parseIntAttribute(parser, ATTR_TITLE_RES_ID); titleResName = ShortcutService.parseStringAttribute(parser, ATTR_TITLE_RES_NAME); - splashScreenThemeResId = ShortcutService.parseIntAttribute(parser, - ATTR_SPLASH_SCREEN_THEME_ID); + splashScreenThemeResName = ShortcutService.parseStringAttribute(parser, + ATTR_SPLASH_SCREEN_THEME_NAME); text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT); textResId = ShortcutService.parseIntAttribute(parser, ATTR_TEXT_RES_ID); textResName = ShortcutService.parseStringAttribute(parser, ATTR_TEXT_RES_NAME); @@ -2117,7 +2117,7 @@ class ShortcutPackage extends ShortcutPackageItem { rank, extras, lastChangedTimestamp, flags, iconResId, iconResName, bitmapPath, iconUri, disabledReason, persons.toArray(new Person[persons.size()]), locusId, - splashScreenThemeResId); + splashScreenThemeResName); } private static Intent parseIntent(TypedXmlPullParser parser) diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java index c06f01a463ad..b86c50b23687 100644 --- a/services/core/java/com/android/server/pm/ShortcutParser.java +++ b/services/core/java/com/android/server/pm/ShortcutParser.java @@ -383,8 +383,11 @@ public class ShortcutParser { final int textResId = sa.getResourceId(R.styleable.Shortcut_shortcutLongLabel, 0); final int disabledMessageResId = sa.getResourceId( R.styleable.Shortcut_shortcutDisabledMessage, 0); - final int splashScreenTheme = sa.getResourceId( + final int splashScreenThemeResId = sa.getResourceId( R.styleable.Shortcut_splashScreenTheme, 0); + final String splashScreenThemeResName = splashScreenThemeResId != 0 + ? service.mContext.getResources().getResourceName(splashScreenThemeResId) + : null; if (TextUtils.isEmpty(id)) { Log.w(TAG, "android:shortcutId must be provided. activity=" + activity); @@ -407,7 +410,7 @@ public class ShortcutParser { rank, iconResId, enabled, - splashScreenTheme); + splashScreenThemeResName); } finally { sa.recycle(); } @@ -416,7 +419,7 @@ public class ShortcutParser { private static ShortcutInfo createShortcutFromManifest(ShortcutService service, @UserIdInt int userId, String id, String packageName, ComponentName activityComponent, int titleResId, int textResId, int disabledMessageResId, - int rank, int iconResId, boolean enabled, int splashScreenTheme) { + int rank, int iconResId, boolean enabled, @Nullable String splashScreenThemeResName) { final int flags = (enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED) @@ -456,7 +459,7 @@ public class ShortcutParser { disabledReason, null /* persons */, null /* locusId */, - splashScreenTheme); + splashScreenThemeResName); } private static String parseCategory(ShortcutService service, AttributeSet attrs) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 5f1027797292..fcbf40e29933 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -3536,7 +3536,8 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public int getShortcutStartingThemeResId(int launcherUserId, + @Nullable + public String getShortcutStartingThemeResName(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId) { Objects.requireNonNull(callingPackage, "callingPackage"); @@ -3553,11 +3554,11 @@ public class ShortcutService extends IShortcutService.Stub { final ShortcutPackage p = getUserShortcutsLocked(userId) .getPackageShortcutsIfExists(packageName); if (p == null) { - return 0; + return null; } final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId); - return shortcutInfo != null ? shortcutInfo.getStartingThemeResId() : 0; + return shortcutInfo != null ? shortcutInfo.getStartingThemeResName() : null; } } diff --git a/services/core/java/com/android/server/pm/SilentUpdatePolicy.java b/services/core/java/com/android/server/pm/SilentUpdatePolicy.java index 117acab6d079..700f72cfbb94 100644 --- a/services/core/java/com/android/server/pm/SilentUpdatePolicy.java +++ b/services/core/java/com/android/server/pm/SilentUpdatePolicy.java @@ -33,7 +33,8 @@ import java.util.concurrent.TimeUnit; * in the {@link PackageInstallerSession}. */ public class SilentUpdatePolicy { - // A throttle time to prevent the installer from silently updating the same app repeatedly. + // The default throttle time to prevent the installer from silently updating the same app + // repeatedly. private static final long SILENT_UPDATE_THROTTLE_TIME_MS = TimeUnit.SECONDS.toMillis(30); // Map to the uptime timestamp for each installer and app of the silent update. @@ -44,6 +45,9 @@ public class SilentUpdatePolicy { @GuardedBy("mSilentUpdateInfos") private String mAllowUnlimitedSilentUpdatesInstaller; + @GuardedBy("mSilentUpdateInfos") + private long mSilentUpdateThrottleTimeMs = SILENT_UPDATE_THROTTLE_TIME_MS; + /** * Checks if the silent update is allowed by the given installer and app package name. * @@ -58,7 +62,11 @@ public class SilentUpdatePolicy { return true; } final long lastSilentUpdatedMs = getTimestampMs(installerPackageName, packageName); - return SystemClock.uptimeMillis() - lastSilentUpdatedMs > SILENT_UPDATE_THROTTLE_TIME_MS; + final long throttleTimeMs; + synchronized (mSilentUpdateInfos) { + throttleTimeMs = mSilentUpdateThrottleTimeMs; + } + return SystemClock.uptimeMillis() - lastSilentUpdatedMs > throttleTimeMs; } /** @@ -99,11 +107,25 @@ public class SilentUpdatePolicy { } } + /** + * Set the silent updates throttle time in seconds. + * + * @param throttleTimeInSeconds The throttle time to set, or <code>-1</code> to restore the + * value to the default. + */ + void setSilentUpdatesThrottleTime(long throttleTimeInSeconds) { + synchronized (mSilentUpdateInfos) { + mSilentUpdateThrottleTimeMs = throttleTimeInSeconds >= 0 + ? TimeUnit.SECONDS.toMillis(throttleTimeInSeconds) + : SILENT_UPDATE_THROTTLE_TIME_MS; + } + } + private void pruneLocked(long uptime) { final int size = mSilentUpdateInfos.size(); for (int i = size - 1; i >= 0; i--) { final long lastSilentUpdatedMs = mSilentUpdateInfos.valueAt(i); - if (uptime - lastSilentUpdatedMs > SILENT_UPDATE_THROTTLE_TIME_MS) { + if (uptime - lastSilentUpdatedMs > mSilentUpdateThrottleTimeMs) { mSilentUpdateInfos.removeAt(i); } } diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index b7a069e10b67..878eb920717e 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -13,6 +13,9 @@ "name": "CtsAppEnumerationTestCases" }, { + "name": "AppEnumerationInternalTests" + }, + { "name": "CtsMatchFlagTestCases" }, { @@ -52,6 +55,14 @@ ] }, { + "name": "GtsContentTestCases", + "options": [ + { + "include-filter": "com.google.android.content.gts" + } + ] + }, + { "name": "GtsSecurityHostTestCases", "options": [ { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index e8897cab14ff..d4feb3a728c8 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3902,6 +3902,17 @@ public class UserManagerService extends IUserManager.Stub { isFirstBoot, isUpgrade, existingPackages); } + @Override + public String[] getPreInstallableSystemPackages(@NonNull String userType) { + checkManageOrCreateUsersPermission("getPreInstallableSystemPackages"); + final Set<String> installableSystemPackages = + mSystemPackageInstaller.getInstallablePackagesForUserType(userType); + if (installableSystemPackages == null) { + return null; + } + return installableSystemPackages.toArray(new String[installableSystemPackages.size()]); + } + private long getCreationTime() { final long now = System.currentTimeMillis(); return (now > EPOCH_PLUS_30_YEARS) ? now : 0; diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 32ba26c2d5ed..58204891293c 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -1034,18 +1034,26 @@ public class DexManager { /** * Deletes all the optimizations files generated by ART. + * This is best effort, and the method will log but not throw errors + * for individual deletes + * * @param packageInfo the package information. + * @return the number of freed bytes or -1 if there was an error in the process. */ - public void deleteOptimizedFiles(ArtPackageInfo packageInfo) { + public long deleteOptimizedFiles(ArtPackageInfo packageInfo) { + long freedBytes = 0; + boolean hadErrors = false; for (String codePath : packageInfo.getCodePaths()) { for (String isa : packageInfo.getInstructionSets()) { try { - mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir()); + freedBytes += mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir()); } catch (InstallerException e) { Log.e(TAG, "Failed deleting oat files for " + codePath, e); + hadErrors = true; } } } + return hadErrors ? -1 : freedBytes; } public static class RegisterDexModuleResult { diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index 94005b2c8361..22c370ef4dbe 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -33,14 +33,15 @@ import android.content.pm.ResolveInfo; import android.location.LocationManagerInternal; import android.net.Uri; import android.os.IBinder; +import android.os.PackageTagsList; import android.os.Process; import android.os.UserHandle; import android.service.voice.VoiceInteractionManagerInternal; import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.DecFunction; @@ -52,9 +53,9 @@ import com.android.internal.util.function.TriFunction; import com.android.internal.util.function.UndecFunction; import com.android.server.LocalServices; +import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** @@ -67,11 +68,10 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat "android:activity_recognition_allow_listed_tags"; private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";"; - private static ArraySet<String> sExpectedTags = new ArraySet<>(new String[] { + private static final ArraySet<String> sExpectedTags = new ArraySet<>(new String[] { "awareness_provider", "activity_recognition_provider", "network_location_provider", "network_location_calibration", "fused_location_provider", "geofencer_provider"}); - @NonNull private final Object mLock = new Object(); @@ -96,13 +96,22 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat */ @GuardedBy("mLock - writes only - see above") @NonNull - private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags = + private final ConcurrentHashMap<Integer, PackageTagsList> mLocationTags = new ConcurrentHashMap<>(); + // location tags can vary per uid - but we merge all tags under an app id into the final data + // structure above + @GuardedBy("mLock") + private final SparseArray<PackageTagsList> mPerUidLocationTags = new SparseArray<>(); + + // activity recognition currently only grabs tags from the APK manifest. we know that the + // manifest is the same for all users, so there's no need to track variations in tags across + // different users. if that logic ever changes, this might need to behave more like location + // tags above. @GuardedBy("mLock - writes only - see above") @NonNull - private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> - mActivityRecognitionTags = new ConcurrentHashMap<>(); + private final ConcurrentHashMap<Integer, PackageTagsList> mActivityRecognitionTags = + new ConcurrentHashMap<>(); public AppOpsPolicy(@NonNull Context context) { mContext = context; @@ -112,13 +121,28 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat final LocationManagerInternal locationManagerInternal = LocalServices.getService( LocationManagerInternal.class); - locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> { - synchronized (mLock) { - updateAllowListedTagsForPackageLocked(providerTagInfo.getUid(), - providerTagInfo.getPackageName(), providerTagInfo.getTags(), - mLocationTags); - } - }); + locationManagerInternal.setLocationPackageTagsListener( + (uid, packageTagsList) -> { + synchronized (mLock) { + if (packageTagsList.isEmpty()) { + mPerUidLocationTags.remove(uid); + } else { + mPerUidLocationTags.set(uid, packageTagsList); + } + + int appId = UserHandle.getAppId(uid); + PackageTagsList.Builder appIdTags = new PackageTagsList.Builder(1); + int size = mPerUidLocationTags.size(); + for (int i = 0; i < size; i++) { + if (UserHandle.getAppId(mPerUidLocationTags.keyAt(i)) == appId) { + appIdTags.add(mPerUidLocationTags.valueAt(i)); + } + } + + updateAllowListedTagsForPackageLocked(appId, appIdTags.build(), + mLocationTags); + } + }); final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); @@ -306,95 +330,30 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat } final String tagsList = resolvedService.serviceInfo.metaData.getString( ACTIVITY_RECOGNITION_TAGS); - if (tagsList != null) { - final String[] tags = tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR); + if (!TextUtils.isEmpty(tagsList)) { + PackageTagsList packageTagsList = new PackageTagsList.Builder(1).add( + resolvedService.serviceInfo.packageName, + Arrays.asList(tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR))).build(); synchronized (mLock) { updateAllowListedTagsForPackageLocked( - resolvedService.serviceInfo.applicationInfo.uid, - resolvedService.serviceInfo.packageName, new ArraySet<>(tags), + UserHandle.getAppId(resolvedService.serviceInfo.applicationInfo.uid), + packageTagsList, mActivityRecognitionTags); } } } - private static void updateAllowListedTagsForPackageLocked(int uid, String packageName, - Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String, - ArraySet<String>>> datastore) { - final int appId = UserHandle.getAppId(uid); - // We make a copy of the per UID state to limit our mutation to one - // operation in the underlying concurrent data structure. - ArrayMap<String, ArraySet<String>> appIdTags = datastore.get(appId); - if (appIdTags != null) { - appIdTags = new ArrayMap<>(appIdTags); - } - - ArraySet<String> packageTags = (appIdTags != null) ? appIdTags.get(packageName) : null; - if (packageTags != null) { - packageTags = new ArraySet<>(packageTags); - } - - if (allowListedTags != null && !allowListedTags.isEmpty()) { - if (packageTags != null) { - packageTags.clear(); - packageTags.addAll(allowListedTags); - } else { - packageTags = new ArraySet<>(allowListedTags); - } - if (appIdTags == null) { - appIdTags = new ArrayMap<>(); - } - - // Remove any invalid tags - boolean nullRemoved = packageTags.remove(null); - boolean nullStrRemoved = packageTags.remove("null"); - boolean emptyRemoved = packageTags.remove(""); - if (nullRemoved || nullStrRemoved || emptyRemoved) { - Log.e(LOG_TAG, "Attempted to add invalid source attribution tag, removed " - + "null: " + nullRemoved + " removed \"null\": " + nullStrRemoved - + " removed empty string: " + emptyRemoved); - } - - appIdTags.put(packageName, packageTags); - datastore.put(appId, appIdTags); - } else if (appIdTags != null) { - appIdTags.remove(packageName); - if (!appIdTags.isEmpty()) { - datastore.put(appId, appIdTags); - } else { - datastore.remove(appId); - } - } + private static void updateAllowListedTagsForPackageLocked(int appId, + PackageTagsList packageTagsList, + ConcurrentHashMap<Integer, PackageTagsList> datastore) { + datastore.put(appId, packageTagsList); } private static boolean isDatasourceAttributionTag(int uid, @NonNull String packageName, - @NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String, - ArraySet<String>>> mappedOps) { + @NonNull String attributionTag, @NonNull Map<Integer, PackageTagsList> mappedOps) { // Only a single lookup from the underlying concurrent data structure - final int appId = UserHandle.getAppId(uid); - final ArrayMap<String, ArraySet<String>> appIdTags = mappedOps.get(appId); - if (appIdTags != null) { - final ArraySet<String> packageTags = appIdTags.get(packageName); - if (packageTags != null && packageTags.contains(attributionTag)) { - if (packageName.equals("com.google.android.gms") - && !sExpectedTags.contains(attributionTag)) { - Log.i("AppOpsDebugRemapping", packageName + " tag " - + attributionTag + " in " + packageTags); - } - return true; - } - if (packageName.equals("com.google.android.gms") - && sExpectedTags.contains(attributionTag)) { - Log.i("AppOpsDebugRemapping", packageName + " tag " + attributionTag - + " NOT in " + packageTags); - } - } else { - if (packageName.equals("com.google.android.gms")) { - Log.i("AppOpsDebugRemapping", "no package tags for uid " + uid - + " package " + packageName); - } - - } - return false; + final PackageTagsList appIdTags = mappedOps.get(UserHandle.getAppId(uid)); + return appIdTags != null && appIdTags.contains(packageName, attributionTag); } private static int resolveLocationOp(int code) { diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java index 5b48abb3e1f2..a5969a88008d 100644 --- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java +++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java @@ -135,7 +135,10 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); - context.registerReceiver(mBroadcastReceiver, filter); + // By default CLOSE_SYSTEM_DIALOGS broadcast is sent only for current user, which is user + // 10 on devices with headless system user enabled. + // In order to receive the broadcast, register the broadcast receiver with UserHandle.ALL. + context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); mHasTelephony = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index ed4a7bf107d1..f72adb609f6f 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -961,11 +961,13 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo @Override public boolean allocateSpaceForUpdate(String packageFile) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); if (!isUpdatableApexSupported()) { Log.i(TAG, "Updatable Apex not supported, " + "allocateSpaceForUpdate does nothing."); return true; } + final long token = Binder.clearCallingIdentity(); try { CompressedApexInfoList apexInfoList = getCompressedApexInfoList(packageFile); ApexManager apexManager = ApexManager.getInstance(); @@ -975,6 +977,8 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo e.rethrowAsRuntimeException(); } catch (IOException | UnsupportedOperationException e) { Slog.e(TAG, "Failed to reserve space for compressed apex: ", e); + } finally { + Binder.restoreCallingIdentity(token); } return false; } diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java index e1e6195ad260..f653e4b26438 100644 --- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java +++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java @@ -30,6 +30,9 @@ public final class ProcfsMemoryUtil { "RssAnon:", "VmSwap:" }; + private static final String[] VMSTAT_KEYS = new String[] { + "oom_kill" + }; private ProcfsMemoryUtil() {} @@ -99,4 +102,22 @@ public final class ProcfsMemoryUtil { public int anonRssInKilobytes; public int swapInKilobytes; } + + /** Reads and parses selected entries of /proc/vmstat. */ + @Nullable + static VmStat readVmStat() { + long[] vmstat = new long[VMSTAT_KEYS.length]; + vmstat[0] = -1; + Process.readProcLines("/proc/vmstat", VMSTAT_KEYS, vmstat); + if (vmstat[0] == -1) { + return null; + } + VmStat result = new VmStat(); + result.oomKillCount = (int) vmstat[0]; + return result; + } + + static final class VmStat { + public int oomKillCount; + } } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index dc868b325900..cd0ce2bd46a7 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -544,6 +544,8 @@ public class StatsPullAtomService extends SystemService { return pullProcessDmabufMemory(atomTag, data); case FrameworkStatsLog.SYSTEM_MEMORY: return pullSystemMemory(atomTag, data); + case FrameworkStatsLog.VMSTAT: + return pullVmStat(atomTag, data); case FrameworkStatsLog.TEMPERATURE: synchronized (mTemperatureLock) { return pullTemperatureLocked(atomTag, data); @@ -842,6 +844,7 @@ public class StatsPullAtomService extends SystemService { registerProcessSystemIonHeapSize(); registerSystemMemory(); registerProcessDmabufMemory(); + registerVmStat(); registerTemperature(); registerCoolingDevice(); registerBinderCallsStats(); @@ -2273,6 +2276,27 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerVmStat() { + int tagId = FrameworkStatsLog.VMSTAT; + mStatsManager.setPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } + + int pullVmStat(int atomTag, List<StatsEvent> pulledData) { + ProcfsMemoryUtil.VmStat vmStat = ProcfsMemoryUtil.readVmStat(); + if (vmStat != null) { + pulledData.add( + FrameworkStatsLog.buildStatsEvent( + atomTag, + vmStat.oomKillCount)); + } + return StatsManager.PULL_SUCCESS; + } + private void registerTemperature() { int tagId = FrameworkStatsLog.TEMPERATURE; mStatsManager.setPullAtomCallback( diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index f7d61367c81e..95a06fcff734 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -453,6 +453,10 @@ public class Vcn extends Handler { for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) { gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot); } + + // Update the mobile data state after updating the subscription snapshot as a change in + // subIds for a subGroup may affect the mobile data state. + handleMobileDataToggled(); } private void handleMobileDataToggled() { diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 4a07c1ac1e39..f756434e981a 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -19,7 +19,10 @@ package com.android.server.vibrator; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.IUidObserver; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioManager; @@ -61,6 +64,9 @@ final class VibrationSettings { private final SettingsObserver mSettingObserver; @VisibleForTesting final UidObserver mUidObserver; + @VisibleForTesting + final UserObserver mUserReceiver; + @GuardedBy("mLock") private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); @@ -94,6 +100,7 @@ final class VibrationSettings { mContext = context; mSettingObserver = new SettingsObserver(handler); mUidObserver = new UidObserver(); + mUserReceiver = new UserObserver(); VibrationEffect clickEffect = createEffectFromResource( com.android.internal.R.array.config_virtualKeyVibePattern); @@ -150,6 +157,7 @@ final class VibrationSettings { } }); + mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING)); registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.APPLY_RAMPING_RINGER)); @@ -457,6 +465,17 @@ final class VibrationSettings { } } + /** Implementation of {@link BroadcastReceiver} to update settings on current user change. */ + @VisibleForTesting + final class UserObserver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { + updateSettings(); + } + } + } + /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */ @VisibleForTesting final class UidObserver extends IUidObserver.Stub { diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 2f0ed19d867a..79706ead555c 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -723,6 +723,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { * VibrationAttributes.USAGE_* values. */ private boolean shouldCancelVibration(VibrationAttributes attrs, int usageFilter) { + if (attrs.getUsage() == VibrationAttributes.USAGE_UNKNOWN) { + // Special case, usage UNKNOWN would match all filters. Instead it should only match if + // it's cancelling that usage specifically, or if cancelling all usages. + return usageFilter == VibrationAttributes.USAGE_UNKNOWN + || usageFilter == VibrationAttributes.USAGE_FILTER_MATCH_ALL; + } return (usageFilter & attrs.getUsage()) == attrs.getUsage(); } @@ -1535,7 +1541,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } private int runCancel() { - cancelVibrate(/* usageFilter= */ -1, mToken); + cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, mToken); return 0; } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 53f1035ee422..782e18b0250c 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -129,7 +129,9 @@ import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; @@ -3140,7 +3142,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private void writeWallpaperAttributes(TypedXmlSerializer out, String tag, + + @VisibleForTesting + void writeWallpaperAttributes(TypedXmlSerializer out, String tag, WallpaperData wallpaper) throws IllegalArgumentException, IllegalStateException, IOException { if (DEBUG) { @@ -3179,6 +3183,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub out.attributeInt(null, "colorValue" + i, wc.toArgb()); } } + + int allColorsCount = wallpaper.primaryColors.getAllColors().size(); + out.attributeInt(null, "allColorsCount", allColorsCount); + if (allColorsCount > 0) { + int index = 0; + for (Map.Entry<Integer, Integer> entry : wallpaper.primaryColors.getAllColors() + .entrySet()) { + out.attributeInt(null, "allColorsValue" + index, entry.getKey()); + out.attributeInt(null, "allColorsPopulation" + index, entry.getValue()); + index++; + } + } + out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints()); } @@ -3410,7 +3427,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper, + @VisibleForTesting + void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper, boolean keepDimensionHints) throws XmlPullParserException { final int id = parser.getAttributeInt(null, "id", -1); if (id != -1) { @@ -3437,7 +3455,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0); wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0); int colorsCount = getAttributeInt(parser, "colorsCount", 0); - if (colorsCount > 0) { + int allColorsCount = getAttributeInt(parser, "allColorsCount", 0); + if (allColorsCount > 0) { + Map<Integer, Integer> allColors = new HashMap<>(allColorsCount); + for (int i = 0; i < allColorsCount; i++) { + int colorInt = getAttributeInt(parser, "allColorsValue" + i, 0); + int population = getAttributeInt(parser, "allColorsPopulation" + i, 0); + allColors.put(colorInt, population); + } + int colorHints = getAttributeInt(parser, "colorHints", 0); + wallpaper.primaryColors = new WallpaperColors(allColors, colorHints); + } else if (colorsCount > 0) { Color primary = null, secondary = null, tertiary = null; for (int i = 0; i < colorsCount; i++) { Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0)); diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 71e31c3a10df..a24319f7a98c 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -703,7 +703,7 @@ final class AccessibilityController { Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation) + " displayId: " + displayContent.getDisplayId()); } - mMagnifedViewport.onRotationChanged(displayContent.getPendingTransaction()); + mMagnifedViewport.onRotationChanged(); mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); } @@ -858,7 +858,7 @@ final class AccessibilityController { private final RectF mTempRectF = new RectF(); - private final Point mTempPoint = new Point(); + private final Point mScreenSize = new Point(); private final Matrix mTempMatrix = new Matrix(); @@ -887,8 +887,8 @@ final class AccessibilityController { if (mDisplayContext.getResources().getConfiguration().isScreenRound()) { mCircularPath = new Path(); - mDisplay.getRealSize(mTempPoint); - final int centerXY = mTempPoint.x / 2; + mDisplay.getRealSize(mScreenSize); + final int centerXY = mScreenSize.x / 2; mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW); } else { mCircularPath = null; @@ -917,9 +917,9 @@ final class AccessibilityController { } void recomputeBounds() { - mDisplay.getRealSize(mTempPoint); - final int screenWidth = mTempPoint.x; - final int screenHeight = mTempPoint.y; + mDisplay.getRealSize(mScreenSize); + final int screenWidth = mScreenSize.x; + final int screenHeight = mScreenSize.y; mMagnificationRegion.set(0, 0, 0, 0); final Region availableBounds = mTempRegion1; @@ -1052,7 +1052,7 @@ final class AccessibilityController { || windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; } - void onRotationChanged(SurfaceControl.Transaction t) { + void onRotationChanged() { // If we are showing the magnification border, hide it immediately so // the user does not see strange artifacts during rotation. The screenshot // used for rotation already has the border. After the rotation is complete @@ -1066,7 +1066,7 @@ final class AccessibilityController { mHandler.sendMessageDelayed(message, delay); } recomputeBounds(); - mWindow.updateSize(t); + mWindow.updateSize(); } void setMagnifiedRegionBorderShown(boolean shown, boolean animate) { @@ -1148,9 +1148,9 @@ final class AccessibilityController { /* ignore */ } mSurfaceControl = surfaceControl; - mDisplay.getRealSize(mTempPoint); + mDisplay.getRealSize(mScreenSize); mBlastBufferQueue = new BLASTBufferQueue(SURFACE_TITLE, mSurfaceControl, - mTempPoint.x, mTempPoint.y, PixelFormat.RGBA_8888); + mScreenSize.x, mScreenSize.y, PixelFormat.RGBA_8888); final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); final int layer = @@ -1224,10 +1224,11 @@ final class AccessibilityController { } } - void updateSize(SurfaceControl.Transaction t) { + void updateSize() { synchronized (mService.mGlobalLock) { - mDisplay.getRealSize(mTempPoint); - t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y); + mDisplay.getRealSize(mScreenSize); + mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y, + PixelFormat.RGBA_8888); invalidate(mDirtyRect); } } @@ -1296,8 +1297,8 @@ final class AccessibilityController { pw.println(prefix + " mBounds= " + mBounds + " mDirtyRect= " + mDirtyRect - + " mWidth= " + mSurfaceControl.getWidth() - + " mHeight= " + mSurfaceControl.getHeight()); + + " mWidth= " + mScreenSize.x + + " mHeight= " + mScreenSize.y); } private final class AnimationController extends Handler { @@ -1541,6 +1542,52 @@ final class AccessibilityController { mEmbeddedDisplayIdList.add(displayId); } + boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) { + int wsLayer = mService.mPolicy.getWindowLayerLw(windowState); + int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(), + true); + return shellLayer >= wsLayer; + } + + int addShellRootsIfAbove(WindowState windowState, ArrayList<ShellRoot> shellRoots, + int shellRootIndex, List<WindowInfo> windows, Set<IBinder> addedWindows, + Region unaccountedSpace, boolean focusedWindowAdded) { + while (shellRootIndex < shellRoots.size() + && shellRootIsAbove(windowState, shellRoots.get(shellRootIndex))) { + ShellRoot shellRoot = shellRoots.get(shellRootIndex); + shellRootIndex++; + final WindowInfo info = shellRoot.getWindowInfo(); + if (info == null) { + continue; + } + + info.layer = addedWindows.size(); + windows.add(info); + addedWindows.add(info.token); + unaccountedSpace.op(info.regionInScreen, unaccountedSpace, + Region.Op.REVERSE_DIFFERENCE); + if (unaccountedSpace.isEmpty() && focusedWindowAdded) { + break; + } + } + return shellRootIndex; + } + + private ArrayList<ShellRoot> getSortedShellRoots( + SparseArray<ShellRoot> originalShellRoots) { + ArrayList<ShellRoot> sortedShellRoots = new ArrayList<>(originalShellRoots.size()); + for (int i = originalShellRoots.size() - 1; i >= 0; --i) { + sortedShellRoots.add(originalShellRoots.valueAt(i)); + } + + sortedShellRoots.sort((left, right) -> + mService.mPolicy.getWindowLayerFromTypeLw(right.getWindowType(), true) + - mService.mPolicy.getWindowLayerFromTypeLw(left.getWindowType(), + true)); + + return sortedShellRoots; + } + /** * Check if windows have changed, and send them to the accessibility subsystem if they have. * @@ -1600,9 +1647,22 @@ final class AccessibilityController { final int visibleWindowCount = visibleWindows.size(); HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>(); + ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots); + // Iterate until we figure out what is touchable for the entire screen. + int shellRootIndex = 0; for (int i = visibleWindowCount - 1; i >= 0; i--) { final WindowState windowState = visibleWindows.valueAt(i); + int prevShellRootIndex = shellRootIndex; + shellRootIndex = addShellRootsIfAbove(windowState, shellRoots, shellRootIndex, + windows, addedWindows, unaccountedSpace, focusedWindowAdded); + + // If a Shell Root was added, it could have accounted for all the space already. + if (shellRootIndex > prevShellRootIndex && unaccountedSpace.isEmpty() + && focusedWindowAdded) { + break; + } + final Region regionInScreen = new Region(); computeWindowRegionInScreen(windowState, regionInScreen); @@ -1626,16 +1686,6 @@ final class AccessibilityController { } } - for (int i = dc.mShellRoots.size() - 1; i >= 0; --i) { - final WindowInfo info = dc.mShellRoots.valueAt(i).getWindowInfo(); - if (info == null) { - continue; - } - info.layer = addedWindows.size(); - windows.add(info); - addedWindows.add(info.token); - } - // Remove child/parent references to windows that were not added. final int windowCount = windows.size(); for (int i = 0; i < windowCount; i++) { diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 14f6fb3ed99a..f6217bc2ffd2 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -206,8 +206,8 @@ class ActivityMetricsLogger { * observer to identify which callbacks belong to a launch event. */ final long mTransitionStartTimeNs; - /** The device uptime in seconds when this transition info is created. */ - final int mCurrentTransitionDeviceUptime; + /** The device uptime in millis when this transition info is created. */ + final long mTransitionDeviceUptimeMs; /** The type can be cold (new process), warm (new activity), or hot (bring to front). */ final int mTransitionType; /** Whether the process was already running when the transition started. */ @@ -277,8 +277,7 @@ class ActivityMetricsLogger { mTransitionType = transitionType; mProcessRunning = processRunning; mProcessSwitch = processSwitch; - mCurrentTransitionDeviceUptime = - (int) TimeUnit.MILLISECONDS.toSeconds(launchingState.mCurrentUpTimeMs); + mTransitionDeviceUptimeMs = launchingState.mCurrentUpTimeMs; setLatestLaunchedActivity(r); launchingState.mAssociatedTransitionInfo = this; if (options != null) { @@ -908,7 +907,7 @@ class ActivityMetricsLogger { final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info); if (info.isInterestingToLoggerAndObserver()) { mLoggerHandler.post(() -> logAppTransition( - info.mCurrentTransitionDeviceUptime, info.mCurrentTransitionDelayMs, + info.mTransitionDeviceUptimeMs, info.mCurrentTransitionDelayMs, infoSnapshot, isHibernating)); } mLoggerHandler.post(() -> logAppDisplayed(infoSnapshot)); @@ -920,7 +919,7 @@ class ActivityMetricsLogger { } // This gets called on another thread without holding the activity manager lock. - private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs, + private void logAppTransition(long transitionDeviceUptimeMs, int currentTransitionDelayMs, TransitionInfoSnapshot info, boolean isHibernating) { final LogMaker builder = new LogMaker(APP_TRANSITION); builder.setPackageName(info.packageName); @@ -937,7 +936,7 @@ class ActivityMetricsLogger { } builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0); builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS, - currentTransitionDeviceUptime); + TimeUnit.MILLISECONDS.toSeconds(transitionDeviceUptimeMs)); builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs); builder.setSubtype(info.reason); if (info.startingWindowDelayMs != INVALID_DELAY) { @@ -972,7 +971,7 @@ class ActivityMetricsLogger { info.launchedActivityName, info.launchedActivityLaunchedFromPackage, isInstantApp, - currentTransitionDeviceUptime * 1000, + transitionDeviceUptimeMs, info.reason, currentTransitionDelayMs, info.startingWindowDelayMs, diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 45da45aafaba..55d1920681c5 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -309,6 +309,7 @@ import android.view.animation.Animation; import android.window.IRemoteTransition; import android.window.SizeConfigurationBuckets; import android.window.SplashScreen; +import android.window.SplashScreenView; import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.TaskSnapshot; import android.window.WindowContainerToken; @@ -646,6 +647,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean mLastContainsDismissKeyguardWindow; private boolean mLastContainsTurnScreenOnWindow; + /** Whether the IME is showing when transitioning away from this activity. */ + boolean mLastImeShown; + /** * A flag to determine if this AR is in the process of closing or entering PIP. This is needed * to help AR know that the app is in the process of closing but hasn't yet started closing on @@ -716,25 +720,29 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean startingMoved; boolean mHandleExitSplashScreen; - @TransferSplashScreenState int mTransferringSplashScreenState = - TRANSFER_SPLASH_SCREEN_IDLE; + @TransferSplashScreenState + int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE; - // Idle, can be triggered to do transfer if needed. + /** Idle, can be triggered to do transfer if needed. */ static final int TRANSFER_SPLASH_SCREEN_IDLE = 0; - // requesting a copy from shell. + + /** Requesting a copy from shell. */ static final int TRANSFER_SPLASH_SCREEN_COPYING = 1; - // attach the splash screen view to activity. + + /** Attach the splash screen view to activity. */ static final int TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT = 2; - // client has taken over splash screen view. + + /** Client has taken over splash screen view. */ static final int TRANSFER_SPLASH_SCREEN_FINISH = 3; - @IntDef(prefix = { "TRANSFER_SPLASH_SCREEN_" }, value = { + @IntDef(prefix = {"TRANSFER_SPLASH_SCREEN_"}, value = { TRANSFER_SPLASH_SCREEN_IDLE, TRANSFER_SPLASH_SCREEN_COPYING, TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT, TRANSFER_SPLASH_SCREEN_FINISH, }) - @interface TransferSplashScreenState {} + @interface TransferSplashScreenState { + } // How long we wait until giving up transfer splash screen. private static final int TRANSFER_SPLASH_SCREEN_TIMEOUT = 2000; @@ -1216,6 +1224,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A TopResumedActivityChangeItem.obtain(onTop)); } catch (RemoteException e) { // If process died, whatever. + Slog.w(TAG, "Failed to send top-resumed=" + onTop + " to " + this, e); return false; } return true; @@ -2196,6 +2205,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A removeStartingWindowAnimation(false /* prepareAnimation */); } + /** + * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} it should clean up any + * remaining reference to this {@link ActivityRecord}'s splash screen. + * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int) + * @see SplashScreenView#remove() + */ + void cleanUpSplashScreen() { + // We only clean up the splash screen if we were supposed to handle it. If it was + // transferred to another activity, the next one will handle the clean up. + if (mHandleExitSplashScreen && !startingMoved + && (mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH + || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE)) { + ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Cleaning splash screen token=%s", this); + mAtmService.mTaskOrganizerController.onAppSplashScreenViewRemoved(getTask()); + } + } + void removeStartingWindow() { removeStartingWindowAnimation(true /* prepareAnimation */); } @@ -3343,6 +3369,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A task.cleanUpActivityReferences(this); clearLastParentBeforePip(); + // Clean up the splash screen if it was still displayed. + cleanUpSplashScreen(); + deferRelaunchUntilPaused = false; frozenBeforeDestroy = false; @@ -4568,7 +4597,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (visible) { displayContent.mOpeningApps.add(this); mEnteringAnimation = true; - } else { + } else if (mVisible) { displayContent.mClosingApps.add(this); mEnteringAnimation = false; } @@ -4696,6 +4725,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A setClientVisible(visible); } + if (!visible) { + final InsetsControlTarget imeInputTarget = mDisplayContent.getImeTarget( + DisplayContent.IME_TARGET_INPUT); + mLastImeShown = imeInputTarget != null && imeInputTarget.getWindow() != null + && imeInputTarget.getWindow().mActivityRecord == this + && mDisplayContent.mInputMethodWindow != null + && mDisplayContent.mInputMethodWindow.isVisible(); + } + final DisplayContent displayContent = getDisplayContent(); if (!displayContent.mClosingApps.contains(this) && !displayContent.mOpeningApps.contains(this)) { @@ -5739,10 +5777,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void onStartingWindowDrawn() { + boolean wasTaskVisible = false; if (task != null) { mSplashScreenStyleEmpty = true; + wasTaskVisible = task.getHasBeenVisible(); task.setHasBeenVisible(true); } + + // The transition may not be executed if the starting process hasn't attached. But if the + // starting window is drawn, the transition can start earlier. Exclude finishing and bubble + // because it may be a trampoline. + if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble + && !mDisplayContent.mAppTransition.isReady() + && !mDisplayContent.mAppTransition.isRunning()) { + // The pending transition state will be cleared after the transition is started, so + // save the state for launching the client later (used by LaunchActivityItem). + mStartingData.mIsTransitionForward = mDisplayContent.isNextTransitionForward(); + mDisplayContent.executeAppTransition(); + } } /** Called when the windows associated app window container are drawn. */ @@ -6455,6 +6507,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A || dc.mChangingContainers.contains(this)); } + boolean isTransitionForward() { + return (mStartingData != null && mStartingData.mIsTransitionForward) + || mDisplayContent.isNextTransitionForward(); + } + private int getAnimationLayer() { // The leash is parented to the animation layer. We need to preserve the z-order by using // the prefix order index, but we boost if necessary. @@ -7504,10 +7561,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (getUid() == SYSTEM_UID) { return false; } - // Do not sandbox to activity window bounds if the feature is disabled. - if (mDisplayContent != null && !mDisplayContent.sandboxDisplayApis()) { - return false; - } // Never apply sandboxing to an app that should be explicitly excluded from the config. if (info != null && info.neverSandboxDisplayApis()) { return false; @@ -8060,8 +8113,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A preserveWindow); final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = ResumeActivityItem.obtain( - mDisplayContent.isNextTransitionForward()); + lifecycleItem = ResumeActivityItem.obtain(isTransitionForward()); } else { lifecycleItem = PauseActivityItem.obtain(); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 7fe0f5be287c..d3d1c1ca6a2b 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -836,7 +836,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { final ClientTransaction clientTransaction = ClientTransaction.obtain( proc.getThread(), r.appToken); - final DisplayContent dc = r.mDisplayContent; + final boolean isTransitionForward = r.isTransitionForward(); clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global @@ -845,7 +845,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(), results, newIntents, - r.takeOptions(), dc.isNextTransitionForward(), + r.takeOptions(), isTransitionForward, proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController, r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken, r.getLaunchedFromBubble())); @@ -853,7 +853,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward()); + lifecycleItem = ResumeActivityItem.obtain(isTransitionForward); } else { lifecycleItem = PauseActivityItem.obtain(); } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index eaebb6f1ce74..4acadb21b5e3 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -623,7 +623,11 @@ public class AppTransitionController { siblings.add(current); boolean canPromote = true; - if (parent == null || !parent.canCreateRemoteAnimationTarget()) { + if (parent == null || !parent.canCreateRemoteAnimationTarget() + // We cannot promote the animation on Task's parent when the task is in + // clearing task in case the animating get stuck when performing the opening + // task that behind it. + || (current.asTask() != null && current.asTask().mInRemoveTask)) { canPromote = false; } else { // In case a descendant of the parent belongs to the other group, we cannot promote diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 81992d8934ed..c7cf4b05564f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -63,6 +63,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; @@ -208,6 +210,7 @@ import android.view.Surface.Rotation; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; +import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManager.DisplayImePolicy; @@ -358,13 +361,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp boolean mIsSizeForced = false; /** - * Overridden display size and metrics to activity window bounds. Set via - * "adb shell wm set-sandbox-display-apis". Default to true, since only disable for debugging. - * @see WindowManagerService#setSandboxDisplayApis(int, boolean) - */ - private boolean mSandboxDisplayApis = true; - - /** * Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity} * but can be set from Settings or via shell command "adb shell wm density". * @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int) @@ -1556,9 +1552,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // to cover the activity configuration change. return false; } - if ((r.mStartingData != null && r.mStartingData.hasImeSurface()) - || (mInsetsStateController.getImeSourceProvider() - .getSource().getVisibleFrame() != null)) { + if (r.attachedToProcess() && mayImeShowOnLaunchingActivity(r)) { // Currently it is unknown that when will IME window be ready. Reject the case to // avoid flickering by showing IME in inconsistent orientation. return false; @@ -1614,6 +1608,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return true; } + /** Returns {@code true} if the IME is possible to show on the launching activity. */ + private boolean mayImeShowOnLaunchingActivity(@NonNull ActivityRecord r) { + final WindowState win = r.findMainWindow(); + if (win == null) { + return false; + } + // See InputMethodManagerService#shouldRestoreImeVisibility that we expecting the IME + // should be hidden when the window set the hidden softInputMode. + final int softInputMode = win.mAttrs.softInputMode; + switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { + case SOFT_INPUT_STATE_ALWAYS_HIDDEN: + case SOFT_INPUT_STATE_HIDDEN: + return false; + } + return r.mLastImeShown && mInputMethodWindow != null && mInputMethodWindow.mHasSurface + && mInputMethodWindow.mViewVisibility == View.VISIBLE; + } + /** Returns {@code true} if the top activity is transformed with the new rotation of display. */ boolean hasTopFixedRotationLaunchingApp() { return mFixedRotationLaunchingApp != null @@ -3821,7 +3833,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // 4. Update the IME control target to apply any inset change and animation. // 5. Reparent the IME container surface to either the input target app, or the IME window // parent. - updateImeControlTarget(); + updateImeControlTarget(true /* forceUpdateImeParent */); } @VisibleForTesting @@ -3953,12 +3965,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void updateImeControlTarget() { + updateImeControlTarget(false /* forceUpdateImeParent */); + } + + void updateImeControlTarget(boolean forceUpdateImeParent) { InsetsControlTarget prevImeControlTarget = mImeControlTarget; mImeControlTarget = computeImeControlTarget(); mInsetsStateController.onImeControlTargetChanged(mImeControlTarget); - // Update Ime parent when IME insets leash created, which is the best time that default - // IME visibility has been settled down after IME control target changed. - if (prevImeControlTarget != mImeControlTarget) { + // Update Ime parent when IME insets leash created or the new IME layering target might + // updated from setImeLayeringTarget, which is the best time that default IME visibility + // has been settled down after IME control target changed. + if (prevImeControlTarget != mImeControlTarget || forceUpdateImeParent) { updateImeParent(); } @@ -5810,21 +5827,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return true; } - /** - * Sets if Display APIs should be sandboxed to the activity window bounds. - */ - void setSandboxDisplayApis(boolean sandboxDisplayApis) { - mSandboxDisplayApis = sandboxDisplayApis; - } - - /** - * Returns {@code true} is Display APIs should be sandboxed to the activity window bounds, - * {@code false} otherwise. Default to true, unless set for debugging purposes. - */ - boolean sandboxDisplayApis() { - return mSandboxDisplayApis; - } - /** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */ class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 977df93412f8..97c19ab72918 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1771,7 +1771,7 @@ public class DisplayPolicy { } // Voice interaction overrides both top fullscreen and top docked. - if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION) { + if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION && attrs.isFullscreen()) { if (mTopFullscreenOpaqueWindowState == null) { mTopFullscreenOpaqueWindowState = win; if (mTopFullscreenOpaqueOrDimmingWindowState == null) { diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 6f2f69810f3f..8c781a13f7db 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -394,10 +394,23 @@ final class InputMonitor { /** * Called when the current input focus changes. */ - private void updateInputFocusRequest() { + private void updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer) { final WindowState focus = mDisplayContent.mCurrentFocus; - final IBinder focusToken = focus != null ? focus.mInputChannelToken : null; + // Request focus for the recents animation input consumer if an input consumer should + // be applied for the window. + if (recentsAnimationInputConsumer != null && focus != null) { + final RecentsAnimationController recentsAnimationController = + mService.getRecentsAnimationController(); + final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null + && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord); + if (shouldApplyRecentsInputConsumer) { + requestFocus(recentsAnimationInputConsumer.mWindowHandle.token, + recentsAnimationInputConsumer.mName); + return; + } + } + final IBinder focusToken = focus != null ? focus.mInputChannelToken : null; if (focusToken == null) { mInputFocus = null; return; @@ -474,8 +487,6 @@ final class InputMonitor { boolean mInDrag; - private boolean mRecentsAnimationFocusOverride; - private void updateInputWindows(boolean inDrag) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows"); @@ -491,16 +502,8 @@ final class InputMonitor { mInDrag = inDrag; resetInputConsumers(mInputTransaction); - mRecentsAnimationFocusOverride = false; mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */); - - if (mRecentsAnimationFocusOverride) { - requestFocus(mRecentsAnimationInputConsumer.mWindowHandle.token, - mRecentsAnimationInputConsumer.mName); - } else { - updateInputFocusRequest(); - } - + updateInputFocusRequest(mRecentsAnimationInputConsumer); if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); @@ -538,7 +541,6 @@ final class InputMonitor { mRecentsAnimationInputConsumer.mWindowHandle)) { mRecentsAnimationInputConsumer.show(mInputTransaction, w.mActivityRecord); mAddRecentsAnimationInputConsumerHandle = false; - mRecentsAnimationFocusOverride = true; } } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 0112f797d937..f8238c1d154a 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -48,6 +48,7 @@ import android.os.Trace; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import android.view.Display; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.server.inputmethod.InputMethodManagerInternal; @@ -163,16 +164,27 @@ class KeyguardController { aodShowing ? 1 : 0, mKeyguardGoingAway ? 1 : 0, "setKeyguardShown"); + + // Update the task snapshot if the screen will not be turned off. To make sure that the + // unlocking animation can animate consistent content. The conditions are: + // - Either AOD or keyguard changes to be showing. So if the states change individually, + // the later one can be skipped to avoid taking snapshot again. While it still accepts + // if both of them change to show at the same time. + // - Keyguard was not going away. Because if it was, the closing transition is able to + // handle the snapshot. + // - The display state is ON. Because if AOD is not on or pulsing, the display state will + // be OFF or DOZE (the path of screen off may have handled it). + if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged)) + && !mKeyguardGoingAway && Display.isOnState( + mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) { + mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY); + } + mKeyguardShowing = keyguardShowing; mAodShowing = aodShowing; if (aodChanged) { // Ensure the new state takes effect. mWindowManager.mWindowPlacerLocked.performSurfacePlacement(); - // If the device can enter AOD and keyguard at the same time, the screen will not be - // turned off, so the snapshot needs to be refreshed when these states are changed. - if (aodShowing && keyguardShowing && keyguardChanged) { - mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY); - } } if (keyguardChanged) { diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index eb7087cbc722..7174e68b06f4 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -21,6 +21,7 @@ import android.content.Context; import android.graphics.Color; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -104,20 +105,12 @@ final class LetterboxConfiguration { * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and * the framework implementation will be used to determine the aspect ratio. */ + @VisibleForTesting void setFixedOrientationLetterboxAspectRatio(float aspectRatio) { mFixedOrientationLetterboxAspectRatio = aspectRatio; } /** - * Resets the aspect ratio of letterbox for fixed orientation to {@link - * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}. - */ - void resetFixedOrientationLetterboxAspectRatio() { - mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( - com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio); - } - - /** * Gets the aspect ratio of letterbox for fixed orientation. */ float getFixedOrientationLetterboxAspectRatio() { @@ -125,25 +118,6 @@ final class LetterboxConfiguration { } /** - * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0, - * both it and a value of {@link - * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and - * and corners of the activity won't be rounded. - */ - void setLetterboxActivityCornersRadius(int cornersRadius) { - mLetterboxActivityCornersRadius = cornersRadius; - } - - /** - * Resets corners raidus for activities presented in the letterbox mode to {@link - * com.android.internal.R.integer.config_letterboxActivityCornersRadius}. - */ - void resetLetterboxActivityCornersRadius() { - mLetterboxActivityCornersRadius = mContext.getResources().getInteger( - com.android.internal.R.integer.config_letterboxActivityCornersRadius); - } - - /** * Whether corners of letterboxed activities are rounded. */ boolean isLetterboxActivityCornersRounded() { @@ -166,25 +140,6 @@ final class LetterboxConfiguration { return mLetterboxBackgroundColor; } - - /** - * Sets color of letterbox background which is used when {@link - * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as - * fallback for other backfround types. - */ - void setLetterboxBackgroundColor(Color color) { - mLetterboxBackgroundColor = color; - } - - /** - * Resets color of letterbox background to {@link - * com.android.internal.R.color.config_letterboxBackgroundColor}. - */ - void resetLetterboxBackgroundColor() { - mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor( - com.android.internal.R.color.config_letterboxBackgroundColor)); - } - /** * Gets {@link LetterboxBackgroundType} specified in {@link * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command. @@ -194,19 +149,6 @@ final class LetterboxConfiguration { return mLetterboxBackgroundType; } - /** Sets letterbox background type. */ - void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) { - mLetterboxBackgroundType = backgroundType; - } - - /** - * Resets cletterbox background type to {@link - * com.android.internal.R.integer.config_letterboxBackgroundType}. - */ - void resetLetterboxBackgroundType() { - mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); - } - /** Returns a string representing the given {@link LetterboxBackgroundType}. */ static String letterboxBackgroundTypeToString( @LetterboxBackgroundType int backgroundType) { @@ -236,27 +178,6 @@ final class LetterboxConfiguration { } /** - * Overrides alpha of a black scrim shown over wallpaper for {@link - * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}. - * - * <p>If given value is < 0 or >= 1, both it and a value of {@link - * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored - * and 0.0 (transparent) is instead. - */ - void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) { - mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha; - } - - /** - * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link - * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}. - */ - void resetLetterboxBackgroundWallpaperDarkScrimAlpha() { - mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat( - com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); - } - - /** * Gets alpha of a black scrim shown over wallpaper letterbox background. */ float getLetterboxBackgroundWallpaperDarkScrimAlpha() { @@ -264,28 +185,6 @@ final class LetterboxConfiguration { } /** - * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in - * {@link mLetterboxBackgroundType}. - * - * <p> If given value <= 0, both it and a value of {@link - * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored - * and 0 is used instead. - */ - void setLetterboxBackgroundWallpaperBlurRadius(int radius) { - mLetterboxBackgroundWallpaperBlurRadius = radius; - } - - /** - * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link - * mLetterboxBackgroundType} to {@link - * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}. - */ - void resetLetterboxBackgroundWallpaperBlurRadius() { - mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius); - } - - /** * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link * mLetterboxBackgroundType}. */ @@ -312,17 +211,9 @@ final class LetterboxConfiguration { * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and * central position (0.5) is used. */ + @VisibleForTesting void setLetterboxHorizontalPositionMultiplier(float multiplier) { mLetterboxHorizontalPositionMultiplier = multiplier; } - /** - * Resets horizontal position of a center of the letterboxed app window to {@link - * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}. - */ - void resetLetterboxHorizontalPositionMultiplier() { - mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat( - com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier); - } - } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ea80b8bb5286..c510603d1d7d 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; @@ -3383,6 +3384,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent> */ void lockAllProfileTasks(@UserIdInt int userId) { forAllLeafTasks(task -> { + final ActivityRecord top = task.topRunningActivity(); + if (top != null && !top.finishing + && ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER.equals(top.intent.getAction()) + && top.packageName.equals( + mService.getSysUiServiceComponentLocked().getPackageName())) { + // Do nothing since the task is already secure by sysui. + return; + } + if (task.getActivity(activity -> !activity.finishing && activity.mUserId == userId) != null) { mService.getTaskChangeNotificationController().notifyTaskProfileLocked( diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java index b56e76d91370..be6a5d2fe27b 100644 --- a/services/core/java/com/android/server/wm/ShellRoot.java +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -50,6 +50,7 @@ public class ShellRoot { private SurfaceControl mSurfaceControl = null; private IWindow mAccessibilityWindow; private IBinder.DeathRecipient mAccessibilityWindowDeath; + private int mWindowType; ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc, @WindowManager.ShellRootLayer final int shellRootLayer) { @@ -64,19 +65,18 @@ public class ShellRoot { return; } mClient = client; - int windowType; switch (shellRootLayer) { case SHELL_ROOT_LAYER_DIVIDER: - windowType = TYPE_DOCK_DIVIDER; + mWindowType = TYPE_DOCK_DIVIDER; break; case SHELL_ROOT_LAYER_PIP: - windowType = TYPE_APPLICATION_OVERLAY; + mWindowType = TYPE_APPLICATION_OVERLAY; break; default: throw new IllegalArgumentException(shellRootLayer + " is not an acceptable shell root layer."); } - mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), windowType) + mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), mWindowType) .setDisplayContent(dc) .setPersistOnEmpty(true) .setOwnerCanManageAppTokens(true) @@ -89,6 +89,10 @@ public class ShellRoot { mToken.getPendingTransaction().show(mSurfaceControl); } + int getWindowType() { + return mWindowType; + } + void clear() { if (mClient != null) { mClient.asBinder().unlinkToDeath(mDeathRecipient, 0); diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java index 59de43ac95a3..3f9c93bbcfbe 100644 --- a/services/core/java/com/android/server/wm/StartingData.java +++ b/services/core/java/com/android/server/wm/StartingData.java @@ -26,6 +26,12 @@ public abstract class StartingData { protected final WindowManagerService mService; protected final int mTypeParams; + /** + * Tell whether the launching activity should use + * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}. + */ + boolean mIsTransitionForward; + protected StartingData(WindowManagerService service, int typeParams) { mService = service; mTypeParams = typeParams; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7af8d8bb5200..565f99f80890 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -6689,24 +6689,26 @@ class Task extends WindowContainer<WindowContainer> { } } - // TODO(185200798): Persist theme name instead of theme if - int splashScreenThemeResId = options != null - ? options.getSplashScreenThemeResId() : 0; - - // User can override the splashscreen theme. The theme name is used to persist - // the setting, so if no theme is set in the ActivityOptions, we check if has - // been persisted here. - if (splashScreenThemeResId == 0) { + // Find the splash screen theme. User can override the persisted theme by + // ActivityOptions. + String splashScreenThemeResName = options != null + ? options.getSplashScreenThemeResName() : null; + if (splashScreenThemeResName == null || splashScreenThemeResName.isEmpty()) { try { - String themeName = mAtmService.getPackageManager() + splashScreenThemeResName = mAtmService.getPackageManager() .getSplashScreenTheme(r.packageName, r.mUserId); - if (themeName != null) { - Context packageContext = mAtmService.mContext - .createPackageContext(r.packageName, 0); - splashScreenThemeResId = packageContext.getResources() - .getIdentifier(themeName, null, null); - } - } catch (RemoteException | PackageManager.NameNotFoundException + } catch (RemoteException ignore) { + // Just use the default theme + } + } + int splashScreenThemeResId = 0; + if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) { + try { + final Context packageContext = mAtmService.mContext + .createPackageContext(r.packageName, 0); + splashScreenThemeResId = packageContext.getResources() + .getIdentifier(splashScreenThemeResName, null, null); + } catch (PackageManager.NameNotFoundException | Resources.NotFoundException ignore) { // Just use the default theme } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index f23028f6f67a..3a2ca80f2e12 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -43,6 +43,7 @@ import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; +import android.window.SplashScreenView; import android.window.StartingWindowInfo; import android.window.TaskAppearedInfo; import android.window.TaskSnapshot; @@ -215,6 +216,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } + void onAppSplashScreenViewRemoved(Task task) { + try { + mTaskOrganizer.onAppSplashScreenViewRemoved(task.mTaskId); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onAppSplashScreenViewRemoved callback", e); + } + } + SurfaceControl prepareLeash(Task task, String reason) { return new SurfaceControl(task.getSurfaceControl(), reason); } @@ -314,6 +323,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mOrganizer.copySplashScreenView(t); } + public void onAppSplashScreenViewRemoved(Task t) { + mOrganizer.onAppSplashScreenViewRemoved(t); + } + /** * Register this task with this state, but doesn't trigger the task appeared callback to * the organizer. @@ -566,6 +579,22 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return true; } + /** + * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has + * removed the splash screen view. + * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int) + * @see SplashScreenView#remove() + */ + public void onAppSplashScreenViewRemoved(Task task) { + final Task rootTask = task.getRootTask(); + if (rootTask == null || rootTask.mTaskOrganizer == null) { + return; + } + final TaskOrganizerState state = + mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder()); + state.onAppSplashScreenViewRemoved(task); + } + void onTaskAppeared(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); if (state != null && state.addTask(task)) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 9ffb2b176915..0000f95e7849 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -443,7 +443,8 @@ class TaskSnapshotController { } else { excludeLayers = new SurfaceControl[0]; } - builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isDrawn()); + builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible()); + final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = SurfaceControl.captureLayersExcluding( task.getSurfaceControl(), mTmpRect, scaleFraction, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6d51849ea513..2c8fcebdd50f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2463,6 +2463,17 @@ public class WindowManagerService extends IWindowManager.Stub configChanged = displayContent.updateOrientation(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + final DisplayInfo rotatedDisplayInfo = + win.mToken.getFixedRotationTransformDisplayInfo(); + if (rotatedDisplayInfo != null) { + outSurfaceControl.setTransformHint(rotatedDisplayInfo.rotation); + } else { + // We have to update the transform hint of display here, but we need to get if from + // SurfaceFlinger, so set it as rotation of display for most cases, then + // SurfaceFlinger would still update the transform hint of display in next frame. + outSurfaceControl.setTransformHint(displayContent.getDisplayInfo().rotation); + } + if (toBeDisplayed && win.mIsWallpaper) { displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */); } @@ -5391,25 +5402,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) { - if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); - } - - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final DisplayContent displayContent = mRoot.getDisplayContent(displayId); - if (displayContent != null) { - displayContent.setSandboxDisplayApis(sandboxDisplayApis); - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - /** The global settings only apply to default display. */ private boolean applyForcedPropertiesForDefaultDisplay() { boolean changed = false; diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index d59654949a27..a94fd074ff2e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -19,12 +19,6 @@ package com.android.server.wm; import static android.os.Build.IS_USER; import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER; - -import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.os.ParcelFileDescriptor; @@ -42,7 +36,6 @@ import com.android.internal.os.ByteTransferPipe; import com.android.internal.protolog.ProtoLogImpl; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; -import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType; import java.io.IOException; import java.io.PrintWriter; @@ -65,12 +58,10 @@ public class WindowManagerShellCommand extends ShellCommand { // Internal service impl -- must perform security checks before touching. private final WindowManagerService mInternal; - private final LetterboxConfiguration mLetterboxConfiguration; public WindowManagerShellCommand(WindowManagerService service) { mInterface = service; mInternal = service; - mLetterboxConfiguration = service.mLetterboxConfiguration; } @Override @@ -122,14 +113,6 @@ public class WindowManagerShellCommand extends ShellCommand { return runGetIgnoreOrientationRequest(pw); case "dump-visible-window-views": return runDumpVisibleWindowViews(pw); - case "set-letterbox-style": - return runSetLetterboxStyle(pw); - case "get-letterbox-style": - return runGetLetterboxStyle(pw); - case "reset-letterbox-style": - return runResetLetterboxStyle(pw); - case "set-sandbox-display-apis": - return runSandboxDisplayApis(pw); case "set-multi-window-config": return runSetMultiWindowConfig(); case "get-multi-window-config": @@ -348,37 +331,6 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } - /** - * Override display size and metrics to reflect the DisplayArea of the calling activity. - */ - private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException { - int displayId = Display.DEFAULT_DISPLAY; - String arg = getNextArgRequired(); - if ("-d".equals(arg)) { - displayId = Integer.parseInt(getNextArgRequired()); - arg = getNextArgRequired(); - } - - final boolean sandboxDisplayApis; - switch (arg) { - case "true": - case "1": - sandboxDisplayApis = true; - break; - case "false": - case "0": - sandboxDisplayApis = false; - break; - default: - getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we " - + "get " + arg); - return -1; - } - - mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis); - return 0; - } - private int runDismissKeyguard(PrintWriter pw) throws RemoteException { mInterface.dismissKeyguard(null /* callback */, null /* message */); return 0; @@ -596,231 +548,6 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } - private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException { - final float aspectRatio; - try { - String arg = getNextArgRequired(); - aspectRatio = Float.parseFloat(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: bad aspect ratio format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: 'reset' or aspect ratio should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio); - } - return 0; - } - - private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException { - final int cornersRadius; - try { - String arg = getNextArgRequired(); - cornersRadius = Integer.parseInt(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: bad corners radius format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: 'reset' or corners radius should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius); - } - return 0; - } - - private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException { - @LetterboxBackgroundType final int backgroundType; - try { - String arg = getNextArgRequired(); - switch (arg) { - case "solid_color": - backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR; - break; - case "app_color_background": - backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; - break; - case "app_color_background_floating": - backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; - break; - case "wallpaper": - backgroundType = LETTERBOX_BACKGROUND_WALLPAPER; - break; - default: - getErrPrintWriter().println( - "Error: 'reset', 'solid_color', 'app_color_background' or " - + "'wallpaper' should be provided as an argument"); - return -1; - } - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: 'reset', 'solid_color', 'app_color_background' or " - + "'wallpaper' should be provided as an argument" + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType); - } - return 0; - } - - private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException { - final Color color; - try { - String arg = getNextArgRequired(); - color = Color.valueOf(Color.parseColor(arg)); - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: 'reset' or color in #RRGGBB format should be provided as " - + "an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundColor(color); - } - return 0; - } - - private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw) - throws RemoteException { - final int radius; - try { - String arg = getNextArgRequired(); - radius = Integer.parseInt(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: blur radius format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: 'reset' or blur radius should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius); - } - return 0; - } - - private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw) - throws RemoteException { - final float alpha; - try { - String arg = getNextArgRequired(); - alpha = Float.parseFloat(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: bad alpha format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: 'reset' or alpha should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha); - } - return 0; - } - - private int runSeLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException { - final float multiplier; - try { - String arg = getNextArgRequired(); - multiplier = Float.parseFloat(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: bad multiplier format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: 'reset' or multiplier should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier); - } - return 0; - } - - private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException { - if (peekNextArg() == null) { - getErrPrintWriter().println("Error: No arguments provided."); - } - while (peekNextArg() != null) { - String arg = getNextArg(); - switch (arg) { - case "--aspectRatio": - runSetFixedOrientationLetterboxAspectRatio(pw); - break; - case "--cornerRadius": - runSetLetterboxActivityCornersRadius(pw); - break; - case "--backgroundType": - runSetLetterboxBackgroundType(pw); - break; - case "--backgroundColor": - runSetLetterboxBackgroundColor(pw); - break; - case "--wallpaperBlurRadius": - runSetLetterboxBackgroundWallpaperBlurRadius(pw); - break; - case "--wallpaperDarkScrimAlpha": - runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw); - break; - case "--horizontalPositionMultiplier": - runSeLetterboxHorizontalPositionMultiplier(pw); - break; - default: - getErrPrintWriter().println( - "Error: Unrecognized letterbox style option: " + arg); - return -1; - } - } - return 0; - } - - private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException { - if (peekNextArg() == null) { - resetLetterboxStyle(); - } - synchronized (mInternal.mGlobalLock) { - while (peekNextArg() != null) { - String arg = getNextArg(); - switch (arg) { - case "aspectRatio": - mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); - break; - case "cornerRadius": - mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); - break; - case "backgroundType": - mLetterboxConfiguration.resetLetterboxBackgroundType(); - break; - case "backgroundColor": - mLetterboxConfiguration.resetLetterboxBackgroundColor(); - break; - case "wallpaperBlurRadius": - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); - break; - case "wallpaperDarkScrimAlpha": - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); - break; - case "horizontalPositionMultiplier": - mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); - break; - default: - getErrPrintWriter().println( - "Error: Unrecognized letterbox style option: " + arg); - return -1; - } - } - } - return 0; - } - private int runSetMultiWindowConfig() { if (peekNextArg() == null) { getErrPrintWriter().println("Error: No arguments provided."); @@ -895,40 +622,6 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } - private void resetLetterboxStyle() { - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); - mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); - mLetterboxConfiguration.resetLetterboxBackgroundType(); - mLetterboxConfiguration.resetLetterboxBackgroundColor(); - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); - mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); - } - } - - private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException { - synchronized (mInternal.mGlobalLock) { - pw.println("Corner radius: " - + mLetterboxConfiguration.getLetterboxActivityCornersRadius()); - pw.println("Horizontal position multiplier: " - + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier()); - pw.println("Aspect ratio: " - + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio()); - - pw.println("Background type: " - + LetterboxConfiguration.letterboxBackgroundTypeToString( - mLetterboxConfiguration.getLetterboxBackgroundType())); - pw.println(" Background color: " + Integer.toHexString( - mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb())); - pw.println(" Wallpaper blur radius: " - + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius()); - pw.println(" Wallpaper dark scrim alpha: " - + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha()); - } - return 0; - } - private int runReset(PrintWriter pw) throws RemoteException { int displayId = getDisplayId(getNextArg()); @@ -953,12 +646,6 @@ public class WindowManagerShellCommand extends ShellCommand { // set-ignore-orientation-request mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */); - // set-letterbox-style - resetLetterboxStyle(); - - // set-sandbox-display-apis - mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true); - // set-multi-window-config runResetMultiWindowConfig(); @@ -993,12 +680,7 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]"); pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] "); pw.println(" If app requested orientation should be ignored."); - pw.println(" set-sandbox-display-apis [true|1|false|0]"); - pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect "); - pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or"); - pw.println(" Size Compat Mode."); - printLetterboxHelp(pw); printMultiWindowConfigHelp(pw); pw.println(" reset [-d DISPLAY_ID]"); @@ -1011,49 +693,6 @@ public class WindowManagerShellCommand extends ShellCommand { } } - private void printLetterboxHelp(PrintWriter pw) { - pw.println(" set-letterbox-style"); - pw.println(" Sets letterbox style using the following options:"); - pw.println(" --aspectRatio aspectRatio"); - pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= " - + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO); - pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will"); - pw.println(" be ignored and framework implementation will determine aspect ratio."); - pw.println(" --cornerRadius radius"); - pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,"); - pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be"); - pw.println(" ignored and corners of the activity won't be rounded."); - pw.println(" --backgroundType [reset|solid_color|app_color_background"); - pw.println(" |app_color_background_floating|wallpaper]"); - pw.println(" Type of background used in the letterbox mode."); - pw.println(" --backgroundColor color"); - pw.println(" Color of letterbox which is be used when letterbox background type"); - pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control"); - pw.println(" letterbox background type. See Color#parseColor for allowed color"); - pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive)."); - pw.println(" --wallpaperBlurRadius radius"); - pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0"); - pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius"); - pw.println(" are ignored and 0 is used."); - pw.println(" --wallpaperDarkScrimAlpha alpha"); - pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'"); - pw.println(" letterbox background. If alpha < 0 or >= 1 both it and"); - pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored"); - pw.println(" and 0.0 (transparent) is used instead."); - pw.println(" --horizontalPositionMultiplier multiplier"); - pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,"); - pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier"); - pw.println(" are ignored and central position (0.5) is used."); - pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType"); - pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha"); - pw.println(" |horizontalPositionMultiplier]"); - pw.println(" Resets overrides to default values for specified properties separated"); - pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'."); - pw.println(" If no arguments provided, all values will be reset."); - pw.println(" get-letterbox-style"); - pw.println(" Prints letterbox style configuration."); - } - private void printMultiWindowConfigHelp(PrintWriter pw) { pw.println(" set-multi-window-config"); pw.println(" Sets options to determine if activity should be shown in multi window:"); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 03762b3ee8bf..bba50a75a3b9 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.LAYOUT_IN_DISPLAY_CUTOUT_M 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_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; +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_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; @@ -2567,6 +2568,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private void setupWindowForRemoveOnExit() { mRemoveOnExit = true; setDisplayLayoutNeeded(); + getDisplayContent().getDisplayPolicy().removeWindowLw(this); // Request a focus update as this window's input channel is already gone. Otherwise // we could have no focused window in input manager. final boolean focusChanged = mWmService.updateFocusedWindowLocked( @@ -5385,6 +5387,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP || mAttrs.type == TYPE_NAVIGATION_BAR_PANEL) { return false; } + if ((mAttrs.privateFlags & PRIVATE_FLAG_NOT_MAGNIFIABLE) != 0) { + return false; + } return true; } @@ -5508,7 +5513,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mSurfacePlacementNeeded = false; transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top, mSurfacePosition); - mSurfacePosition.offset(mXOffset, mYOffset); + + if (mWallpaperScale != 1f) { + DisplayInfo displayInfo = getDisplayInfo(); + Matrix matrix = mTmpMatrix; + matrix.setTranslate(mXOffset, mYOffset); + matrix.postScale(mWallpaperScale, mWallpaperScale, displayInfo.logicalWidth / 2f, + displayInfo.logicalHeight / 2f); + matrix.getValues(mTmpMatrixArray); + mSurfacePosition.offset(Math.round(mTmpMatrixArray[Matrix.MTRANS_X]), + Math.round(mTmpMatrixArray[Matrix.MTRANS_Y])); + } else { + mSurfacePosition.offset(mXOffset, mYOffset); + } // Freeze position while we're unrotated, so the surface remains at the position it was // prior to the rotation. diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index e13ef99e17dd..a94ad4ad7ef2 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -997,10 +997,7 @@ void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration } void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) { - // Set an empty list to remove all handles from the specific display. - std::vector<sp<InputWindowHandle>> windowHandles; - mInputManager->getDispatcher()->setInputWindows({{displayId, windowHandles}}); - mInputManager->getDispatcher()->setFocusedApplication(displayId, nullptr); + mInputManager->getDispatcher()->displayRemoved(displayId); } void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId, diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp index d0f56e62f487..63b7dfbc2a3b 100644 --- a/services/core/jni/com_android_server_sensor_SensorService.cpp +++ b/services/core/jni/com_android_server_sensor_SensorService.cpp @@ -32,6 +32,7 @@ namespace android { +static JavaVM* sJvm = nullptr; static jmethodID sMethodIdOnProximityActive; class NativeSensorService { @@ -93,8 +94,8 @@ NativeSensorService::ProximityActiveListenerDelegate::~ProximityActiveListenerDe } void NativeSensorService::ProximityActiveListenerDelegate::onProximityActive(bool isActive) { - AndroidRuntime::getJNIEnv()->CallVoidMethod(mListener, sMethodIdOnProximityActive, - static_cast<jboolean>(isActive)); + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + jniEnv->CallVoidMethod(mListener, sMethodIdOnProximityActive, static_cast<jboolean>(isActive)); } static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) { @@ -128,7 +129,8 @@ static const JNINativeMethod methods[] = { }; -int register_android_server_sensor_SensorService(JNIEnv* env) { +int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) { + sJvm = vm; jclass listenerClass = FindClassOrDie(env, PROXIMITY_ACTIVE_CLASS); sMethodIdOnProximityActive = GetMethodIDOrDie(env, listenerClass, "onProximityActive", "(Z)V"); return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods, diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index b8961d5ba9b0..ff61abc4ff7f 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -62,7 +62,7 @@ int register_android_server_AdbDebuggingManager(JNIEnv* env); int register_android_server_FaceService(JNIEnv* env); int register_android_server_GpuService(JNIEnv* env); int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); -int register_android_server_sensor_SensorService(JNIEnv* env); +int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env); }; using namespace android; @@ -118,6 +118,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_FaceService(env); register_android_server_GpuService(env); register_android_server_stats_pull_StatsPullAtomService(env); - register_android_server_sensor_SensorService(env); + register_android_server_sensor_SensorService(vm, env); return JNI_VERSION_1_4; } 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 c6dfe9dbbfc2..82aaa61527d1 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -77,6 +77,10 @@ <xs:annotation name="final"/> </xs:element> <xs:element name="timing" type="hbmTiming" minOccurs="1" maxOccurs="1"/> + <xs:element type="refreshRateRange" name="refreshRate" minOccurs="0" maxOccurs="1"> + <xs:annotation name="nullable"/> + <xs:annotation name="final"/> + </xs:element> </xs:all> <xs:attribute name="enabled" type="xs:boolean" use="optional"/> </xs:complexType> @@ -129,14 +133,30 @@ <xs:complexType name="sensorDetails"> <xs:sequence> <xs:element type="xs:string" name="type" minOccurs="0" maxOccurs="1"> - <xs:annotation name="nullable" /> + <xs:annotation name="nullable"/> <xs:annotation name="final"/> </xs:element> <xs:element type="xs:string" name="name" minOccurs="0" maxOccurs="1"> - <xs:annotation name="nullable" /> + <xs:annotation name="nullable"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element type="refreshRateRange" name="refreshRate" minOccurs="0" maxOccurs="1"> + <xs:annotation name="nullable"/> <xs:annotation name="final"/> </xs:element> </xs:sequence> </xs:complexType> + <xs:complexType name="refreshRateRange"> + <xs:sequence> + <xs:element type="xs:nonNegativeInteger" name="minimum" minOccurs="1" maxOccurs="1"> + <xs:annotation name="final"/> + </xs:element> + <xs:element type="xs:nonNegativeInteger" name="maximum" minOccurs="1" maxOccurs="1"> + <xs:annotation name="final"/> + </xs:element> + </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 7c2436db8694..6e2e3625f60c 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -44,10 +44,12 @@ package com.android.server.display.config { ctor public HighBrightnessMode(); method public boolean getEnabled(); method @NonNull public final java.math.BigDecimal getMinimumLux_all(); + method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate_all(); method public com.android.server.display.config.HbmTiming getTiming_all(); method @NonNull public final java.math.BigDecimal getTransitionPoint_all(); method public void setEnabled(boolean); method public final void setMinimumLux_all(@NonNull java.math.BigDecimal); + method public final void setRefreshRate_all(@Nullable com.android.server.display.config.RefreshRateRange); method public void setTiming_all(com.android.server.display.config.HbmTiming); method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal); } @@ -65,11 +67,21 @@ package com.android.server.display.config { method public final void setValue(@NonNull java.math.BigDecimal); } + public class RefreshRateRange { + ctor public RefreshRateRange(); + method public final java.math.BigInteger getMaximum(); + method public final java.math.BigInteger getMinimum(); + method public final void setMaximum(java.math.BigInteger); + method public final void setMinimum(java.math.BigInteger); + } + public class SensorDetails { ctor public SensorDetails(); method @Nullable public final String getName(); + method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate(); method @Nullable public final String getType(); method public final void setName(@Nullable String); + method public final void setRefreshRate(@Nullable com.android.server.display.config.RefreshRateRange); method public final void setType(@Nullable String); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 0128d350bd10..78ad59f8c01b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -169,6 +169,7 @@ import android.app.admin.DevicePolicyManager.OperationSafetyReason; import android.app.admin.DevicePolicyManager.PasswordComplexity; import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason; import android.app.admin.DevicePolicyManagerInternal; +import android.app.admin.DevicePolicyManagerLiteInternal; import android.app.admin.DevicePolicySafetyChecker; import android.app.admin.DeviceStateCache; import android.app.admin.FactoryResetProtectionPolicy; @@ -1748,6 +1749,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager(); mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector); + // "Lite" interface is available even when the device doesn't have the feature + LocalServices.addService(DevicePolicyManagerLiteInternal.class, mLocalService); if (!mHasFeature) { // Skip the rest of the initialization mSetupContentObserver = null; @@ -6832,7 +6835,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void wipeDataWithReason(int flags, String wipeReasonForUser, boolean calledOnParentInstance) { - if (!mHasFeature) { + if (!mHasFeature && !hasCallingOrSelfPermission(permission.MASTER_CLEAR)) { return; } final CallerIdentity caller = getCallerIdentity(); @@ -12612,7 +12615,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @VisibleForTesting - final class LocalService extends DevicePolicyManagerInternal { + final class LocalService extends DevicePolicyManagerInternal + implements DevicePolicyManagerLiteInternal { private List<OnCrossProfileWidgetProvidersChangeListener> mWidgetProviderListeners; @Override @@ -14313,7 +14317,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public boolean isAffiliatedUser() { + public boolean isCallingUserAffiliated() { if (!mHasFeature) { return false; } @@ -14323,6 +14327,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + @Override + public boolean isAffiliatedUser(@UserIdInt int userId) { + if (!mHasFeature) { + return false; + } + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId)); + + return isUserAffiliatedWithDeviceLocked(userId); + } + private boolean isUserAffiliatedWithDeviceLocked(@UserIdInt int userId) { if (!mOwners.hasDeviceOwner()) { return false; @@ -17550,10 +17565,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isUsbDataSignalingEnabled(String packageName) { + final CallerIdentity caller = getCallerIdentity(packageName); synchronized (getLockObject()) { - final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked( - getCallerIdentity(packageName)); - return admin.mUsbDataSignalingEnabled; + // If the caller is an admin, return the policy set by itself. Otherwise + // return the device-wide policy. + if (isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) { + return getProfileOwnerOrDeviceOwnerLocked(caller).mUsbDataSignalingEnabled; + } else { + return isUsbDataSignalingEnabledInternalLocked(); + } } } @@ -17563,12 +17583,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(isSystemUid(caller)); synchronized (getLockObject()) { - final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( - UserHandle.USER_SYSTEM); - return admin == null || admin.mUsbDataSignalingEnabled; + return isUsbDataSignalingEnabledInternalLocked(); } } + private boolean isUsbDataSignalingEnabledInternalLocked() { + final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( + UserHandle.USER_SYSTEM); + return admin == null || admin.mUsbDataSignalingEnabled; + } + @Override public boolean canUsbDataSignalingBeDisabled() { return mInjector.binderWithCleanCallingIdentity(() -> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java index 86437a27a64d..1cbc634b7152 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java @@ -21,7 +21,7 @@ import static android.app.admin.DevicePolicyManager.operationToString; import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.DevicePolicyManager.OperationSafetyReason; -import android.app.admin.DevicePolicyManagerInternal; +import android.app.admin.DevicePolicyManagerLiteInternal; import android.app.admin.DevicePolicySafetyChecker; import android.os.Handler; import android.os.Looper; @@ -80,8 +80,8 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { + ", should be " + operationToString(mOperation)); } String reasonName = operationSafetyReasonToString(reason); - DevicePolicyManagerInternal dpmi = LocalServices - .getService(DevicePolicyManagerInternal.class); + DevicePolicyManagerLiteInternal dpmi = LocalServices + .getService(DevicePolicyManagerLiteInternal.class); Slog.i(TAG, "notifying " + reasonName + " is UNSAFE"); dpmi.notifyUnsafeOperationStateChanged(this, reason, /* isSafe= */ false); diff --git a/services/net/Android.bp b/services/net/Android.bp index f92db86bb880..dd864aed4e8e 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -37,15 +37,7 @@ java_library { name: "services.net-module-wifi", srcs: [ ":framework-services-net-module-wifi-shared-srcs", - ":net-module-utils-srcs", ":net-utils-services-common-srcs", - "java/android/net/ip/IpClientCallbacks.java", - "java/android/net/ip/IpClientManager.java", - "java/android/net/ip/IpClientUtil.java", - "java/android/net/util/KeepalivePacketDataUtil.java", - "java/android/net/util/NetworkConstants.java", - "java/android/net/IpMemoryStore.java", - "java/android/net/NetworkMonitorManager.java", ], sdk_version: "module_current", min_sdk_version: "30", @@ -89,11 +81,7 @@ filegroup { filegroup { name: "services-connectivity-shared-srcs", srcs: [ - // TODO: move to networkstack-client - "java/android/net/IpMemoryStore.java", - "java/android/net/NetworkMonitorManager.java", // TODO: move to libs/net - "java/android/net/util/KeepalivePacketDataUtil.java", "java/android/net/util/NetworkConstants.java", ], } diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java deleted file mode 100644 index 8df2e0d08e0e..000000000000 --- a/services/net/java/android/net/IpMemoryStore.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.annotation.NonNull; -import android.content.Context; -import android.net.networkstack.ModuleNetworkStackClient; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -/** - * Manager class used to communicate with the ip memory store service in the network stack, - * which is running in a separate module. - * @hide -*/ -public class IpMemoryStore extends IpMemoryStoreClient { - private static final String TAG = IpMemoryStore.class.getSimpleName(); - @NonNull private final CompletableFuture<IIpMemoryStore> mService; - @NonNull private final AtomicReference<CompletableFuture<IIpMemoryStore>> mTailNode; - - public IpMemoryStore(@NonNull final Context context) { - super(context); - mService = new CompletableFuture<>(); - mTailNode = new AtomicReference<CompletableFuture<IIpMemoryStore>>(mService); - getModuleNetworkStackClient(context).fetchIpMemoryStore( - new IIpMemoryStoreCallbacks.Stub() { - @Override - public void onIpMemoryStoreFetched(@NonNull final IIpMemoryStore memoryStore) { - mService.complete(memoryStore); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - - @Override - public String getInterfaceHash() { - return this.HASH; - } - }); - } - - /* - * If the IpMemoryStore is ready, this function will run the request synchronously. - * Otherwise, it will enqueue the requests for execution immediately after the - * service becomes ready. The requests are guaranteed to be executed in the order - * they are sumbitted. - */ - @Override - protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException { - mTailNode.getAndUpdate(future -> future.handle((store, exception) -> { - if (exception != null) { - // this should never happens since we also catch the exception below - Log.wtf(TAG, "Error fetching IpMemoryStore", exception); - return store; - } - - try { - cb.accept(store); - } catch (Exception e) { - Log.wtf(TAG, "Exception occured: " + e.getMessage()); - } - return store; - })); - } - - @VisibleForTesting - protected ModuleNetworkStackClient getModuleNetworkStackClient(Context context) { - return ModuleNetworkStackClient.getInstance(context); - } - - /** Gets an instance of the memory store */ - @NonNull - public static IpMemoryStore getMemoryStore(final Context context) { - return new IpMemoryStore(context); - } -} diff --git a/services/net/java/android/net/NetworkMonitorManager.java b/services/net/java/android/net/NetworkMonitorManager.java deleted file mode 100644 index 0f669817f52e..000000000000 --- a/services/net/java/android/net/NetworkMonitorManager.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.annotation.Hide; -import android.annotation.NonNull; -import android.os.Binder; -import android.os.RemoteException; -import android.util.Log; - -/** - * A convenience wrapper for INetworkMonitor. - * - * Wraps INetworkMonitor calls, making them a bit more friendly to use. Currently handles: - * - Clearing calling identity - * - Ignoring RemoteExceptions - * - Converting to stable parcelables - * - * By design, all methods on INetworkMonitor are asynchronous oneway IPCs and are thus void. All the - * wrapper methods in this class return a boolean that callers can use to determine whether - * RemoteException was thrown. - */ -@Hide -public class NetworkMonitorManager { - - @NonNull private final INetworkMonitor mNetworkMonitor; - @NonNull private final String mTag; - - public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager, - @NonNull String tag) { - mNetworkMonitor = networkMonitorManager; - mTag = tag; - } - - public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager) { - this(networkMonitorManager, NetworkMonitorManager.class.getSimpleName()); - } - - private void log(String s, Throwable e) { - Log.e(mTag, s, e); - } - - // CHECKSTYLE:OFF Generated code - - public boolean start() { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.start(); - return true; - } catch (RemoteException e) { - log("Error in start", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean launchCaptivePortalApp() { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.launchCaptivePortalApp(); - return true; - } catch (RemoteException e) { - log("Error in launchCaptivePortalApp", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyCaptivePortalAppFinished(int response) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyCaptivePortalAppFinished(response); - return true; - } catch (RemoteException e) { - log("Error in notifyCaptivePortalAppFinished", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean setAcceptPartialConnectivity() { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.setAcceptPartialConnectivity(); - return true; - } catch (RemoteException e) { - log("Error in setAcceptPartialConnectivity", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean forceReevaluation(int uid) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.forceReevaluation(uid); - return true; - } catch (RemoteException e) { - log("Error in forceReevaluation", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyPrivateDnsChanged(PrivateDnsConfigParcel config) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyPrivateDnsChanged(config); - return true; - } catch (RemoteException e) { - log("Error in notifyPrivateDnsChanged", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyDnsResponse(int returnCode) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyDnsResponse(returnCode); - return true; - } catch (RemoteException e) { - log("Error in notifyDnsResponse", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyNetworkConnected(lp, nc); - return true; - } catch (RemoteException e) { - log("Error in notifyNetworkConnected", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyNetworkDisconnected() { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyNetworkDisconnected(); - return true; - } catch (RemoteException e) { - log("Error in notifyNetworkDisconnected", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyLinkPropertiesChanged(LinkProperties lp) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyLinkPropertiesChanged(lp); - return true; - } catch (RemoteException e) { - log("Error in notifyLinkPropertiesChanged", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyNetworkCapabilitiesChanged(NetworkCapabilities nc) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyNetworkCapabilitiesChanged(nc); - return true; - } catch (RemoteException e) { - log("Error in notifyNetworkCapabilitiesChanged", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - // CHECKSTYLE:ON Generated code -} diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java deleted file mode 100644 index b17fcaa132a1..000000000000 --- a/services/net/java/android/net/ip/IpClientCallbacks.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import android.net.DhcpResultsParcelable; -import android.net.Layer2PacketParcelable; -import android.net.LinkProperties; - -import java.util.List; - -/** - * Callbacks for handling IpClient events. - * - * This is a convenience class to allow clients not to override all methods of IIpClientCallbacks, - * and avoid unparceling arguments. - * These methods are called asynchronously on a Binder thread, as IpClient lives in a different - * process. - * @hide - */ -public class IpClientCallbacks { - - /** - * Callback called upon IpClient creation. - * - * @param ipClient The Binder token to communicate with IpClient. - */ - public void onIpClientCreated(IIpClient ipClient) {} - - /** - * Callback called prior to DHCP discovery/renewal. - * - * <p>In order to receive onPreDhcpAction(), call #withPreDhcpAction() when constructing a - * ProvisioningConfiguration. - * - * <p>Implementations of onPreDhcpAction() must call IpClient#completedPreDhcpAction() to - * indicate that DHCP is clear to proceed. - */ - public void onPreDhcpAction() {} - - /** - * Callback called after DHCP discovery/renewal. - */ - public void onPostDhcpAction() {} - - /** - * Callback called when new DHCP results are available. - * - * <p>This is purely advisory and not an indication of provisioning success or failure. This is - * only here for callers that want to expose DHCPv4 results to other APIs - * (e.g., WifiInfo#setInetAddress). - * - * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not - * the passed-in DhcpResults object is null. - */ - public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) { - // In general callbacks would not use a parcelable directly (DhcpResultsParcelable), and - // would use a wrapper instead, because of the lack of safety of stable parcelables. But - // there are already two classes in the tree for DHCP information: DhcpInfo and DhcpResults, - // and neither of them exposes an appropriate API (they are bags of mutable fields and can't - // be changed because they are public API and @UnsupportedAppUsage, being no better than the - // stable parcelable). Adding a third class would cost more than the gain considering that - // the only client of this callback is WiFi, which will end up converting the results to - // DhcpInfo anyway. - } - - /** - * Indicates that provisioning was successful. - */ - public void onProvisioningSuccess(LinkProperties newLp) {} - - /** - * Indicates that provisioning failed. - */ - public void onProvisioningFailure(LinkProperties newLp) {} - - /** - * Invoked on LinkProperties changes. - */ - public void onLinkPropertiesChange(LinkProperties newLp) {} - - /**Called when the internal IpReachabilityMonitor (if enabled) has - * detected the loss of a critical number of required neighbors. - */ - public void onReachabilityLost(String logMsg) {} - - /** - * Called when the IpClient state machine terminates. - */ - public void onQuit() {} - - /** - * Called to indicate that a new APF program must be installed to filter incoming packets. - */ - public void installPacketFilter(byte[] filter) {} - - /** - * Called to indicate that the APF Program & data buffer must be read asynchronously from the - * wifi driver. - * - * <p>Due to Wifi HAL limitations, the current implementation only supports dumping the entire - * buffer. In response to this request, the driver returns the data buffer asynchronously - * by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message. - */ - public void startReadPacketFilter() {} - - /** - * If multicast filtering cannot be accomplished with APF, this function will be called to - * actuate multicast filtering using another means. - */ - public void setFallbackMulticastFilter(boolean enabled) {} - - /** - * Enabled/disable Neighbor Discover offload functionality. This is called, for example, - * whenever 464xlat is being started or stopped. - */ - public void setNeighborDiscoveryOffload(boolean enable) {} - - /** - * Invoked on starting preconnection process. - */ - public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {} -} diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java deleted file mode 100644 index b45405f39f0f..000000000000 --- a/services/net/java/android/net/ip/IpClientManager.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import android.annotation.Hide; -import android.annotation.NonNull; -import android.net.NattKeepalivePacketData; -import android.net.ProxyInfo; -import android.net.TcpKeepalivePacketData; -import android.net.TcpKeepalivePacketDataParcelable; -import android.net.shared.Layer2Information; -import android.net.shared.ProvisioningConfiguration; -import android.net.util.KeepalivePacketDataUtil; -import android.os.Binder; -import android.os.RemoteException; -import android.util.Log; - -/** - * A convenience wrapper for IpClient. - * - * Wraps IIpClient calls, making them a bit more friendly to use. Currently handles: - * - Clearing calling identity - * - Ignoring RemoteExceptions - * - Converting to stable parcelables - * - * By design, all methods on IIpClient are asynchronous oneway IPCs and are thus void. All the - * wrapper methods in this class return a boolean that callers can use to determine whether - * RemoteException was thrown. - */ -@Hide -public class IpClientManager { - @NonNull private final IIpClient mIpClient; - @NonNull private final String mTag; - - public IpClientManager(@NonNull IIpClient ipClient, @NonNull String tag) { - mIpClient = ipClient; - mTag = tag; - } - - public IpClientManager(@NonNull IIpClient ipClient) { - this(ipClient, IpClientManager.class.getSimpleName()); - } - - private void log(String s, Throwable e) { - Log.e(mTag, s, e); - } - - /** - * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be - * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to - * proceed. - */ - public boolean completedPreDhcpAction() { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.completedPreDhcpAction(); - return true; - } catch (RemoteException e) { - log("Error completing PreDhcpAction", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Confirm the provisioning configuration. - */ - public boolean confirmConfiguration() { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.confirmConfiguration(); - return true; - } catch (RemoteException e) { - log("Error confirming IpClient configuration", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Indicate that packet filter read is complete. - */ - public boolean readPacketFilterComplete(byte[] data) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.readPacketFilterComplete(data); - return true; - } catch (RemoteException e) { - log("Error notifying IpClient of packet filter read", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Shut down this IpClient instance altogether. - */ - public boolean shutdown() { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.shutdown(); - return true; - } catch (RemoteException e) { - log("Error shutting down IpClient", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Start provisioning with the provided parameters. - */ - public boolean startProvisioning(ProvisioningConfiguration prov) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.startProvisioning(prov.toStableParcelable()); - return true; - } catch (RemoteException e) { - log("Error starting IpClient provisioning", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Stop this IpClient. - * - * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}. - */ - public boolean stop() { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.stop(); - return true; - } catch (RemoteException e) { - log("Error stopping IpClient", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Set the TCP buffer sizes to use. - * - * This may be called, repeatedly, at any time before or after a call to - * #startProvisioning(). The setting is cleared upon calling #stop(). - */ - public boolean setTcpBufferSizes(String tcpBufferSizes) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.setTcpBufferSizes(tcpBufferSizes); - return true; - } catch (RemoteException e) { - log("Error setting IpClient TCP buffer sizes", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Set the HTTP Proxy configuration to use. - * - * This may be called, repeatedly, at any time before or after a call to - * #startProvisioning(). The setting is cleared upon calling #stop(). - */ - public boolean setHttpProxy(ProxyInfo proxyInfo) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.setHttpProxy(proxyInfo); - return true; - } catch (RemoteException e) { - log("Error setting IpClient proxy", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering, - * if not, Callback.setFallbackMulticastFilter() is called. - */ - public boolean setMulticastFilter(boolean enabled) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.setMulticastFilter(enabled); - return true; - } catch (RemoteException e) { - log("Error setting multicast filter", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Add a TCP keepalive packet filter before setting up keepalive offload. - */ - public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketData pkt) { - return addKeepalivePacketFilter(slot, KeepalivePacketDataUtil.toStableParcelable(pkt)); - } - - /** - * Add a TCP keepalive packet filter before setting up keepalive offload. - * @deprecated This method is for use on pre-S platforms where TcpKeepalivePacketData is not - * system API. On newer platforms use - * addKeepalivePacketFilter(int, TcpKeepalivePacketData) instead. - */ - @Deprecated - public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.addKeepalivePacketFilter(slot, pkt); - return true; - } catch (RemoteException e) { - log("Error adding Keepalive Packet Filter ", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Add a NAT-T keepalive packet filter before setting up keepalive offload. - */ - public boolean addKeepalivePacketFilter(int slot, NattKeepalivePacketData pkt) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.addNattKeepalivePacketFilter( - slot, KeepalivePacketDataUtil.toStableParcelable(pkt)); - return true; - } catch (RemoteException e) { - log("Error adding NAT-T Keepalive Packet Filter ", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Remove a keepalive packet filter after stopping keepalive offload. - */ - public boolean removeKeepalivePacketFilter(int slot) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.removeKeepalivePacketFilter(slot); - return true; - } catch (RemoteException e) { - log("Error removing Keepalive Packet Filter ", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Set the L2 key and group hint for storing info into the memory store. - */ - public boolean setL2KeyAndGroupHint(String l2Key, String groupHint) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.setL2KeyAndGroupHint(l2Key, groupHint); - return true; - } catch (RemoteException e) { - log("Failed setL2KeyAndGroupHint", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Notify IpClient that preconnection is complete and that the link is ready for use. - * The success parameter indicates whether the packets passed in by 'onPreconnectionStart' - * were successfully sent to the network or not. - */ - public boolean notifyPreconnectionComplete(boolean success) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.notifyPreconnectionComplete(success); - return true; - } catch (RemoteException e) { - log("Error notifying IpClient Preconnection completed", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Update the bssid, L2 key and group hint layer2 information. - */ - public boolean updateLayer2Information(Layer2Information info) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.updateLayer2Information(info.toStableParcelable()); - return true; - } catch (RemoteException e) { - log("Error updating layer2 information", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } -} diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java deleted file mode 100644 index 426614ec2f53..000000000000 --- a/services/net/java/android/net/ip/IpClientUtil.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import android.content.Context; -import android.net.DhcpResultsParcelable; -import android.net.Layer2PacketParcelable; -import android.net.LinkProperties; -import android.net.networkstack.ModuleNetworkStackClient; -import android.os.ConditionVariable; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; - - -/** - * Utilities and wrappers to simplify communication with IpClient, which lives in the NetworkStack - * process. - * - * @hide - */ -public class IpClientUtil { - // TODO: remove with its callers - public static final String DUMP_ARG = "ipclient"; - - /** - * Subclass of {@link IpClientCallbacks} allowing clients to block until provisioning is - * complete with {@link WaitForProvisioningCallbacks#waitForProvisioning()}. - */ - public static class WaitForProvisioningCallbacks extends IpClientCallbacks { - private final ConditionVariable mCV = new ConditionVariable(); - private LinkProperties mCallbackLinkProperties; - - /** - * Block until either {@link #onProvisioningSuccess(LinkProperties)} or - * {@link #onProvisioningFailure(LinkProperties)} is called. - */ - public LinkProperties waitForProvisioning() { - mCV.block(); - return mCallbackLinkProperties; - } - - @Override - public void onProvisioningSuccess(LinkProperties newLp) { - mCallbackLinkProperties = newLp; - mCV.open(); - } - - @Override - public void onProvisioningFailure(LinkProperties newLp) { - mCallbackLinkProperties = null; - mCV.open(); - } - } - - /** - * Create a new IpClient. - * - * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of - * {@link IIpClientCallbacks}. - * @see {@link ModuleNetworkStackClient#makeIpClient(String, IIpClientCallbacks)} - */ - public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) { - ModuleNetworkStackClient.getInstance(context) - .makeIpClient(ifName, new IpClientCallbacksProxy(callback)); - } - - /** - * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}. - */ - private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub { - protected final IpClientCallbacks mCb; - - /** - * Create a new IpClientCallbacksProxy. - */ - public IpClientCallbacksProxy(IpClientCallbacks cb) { - mCb = cb; - } - - @Override - public void onIpClientCreated(IIpClient ipClient) { - mCb.onIpClientCreated(ipClient); - } - - @Override - public void onPreDhcpAction() { - mCb.onPreDhcpAction(); - } - - @Override - public void onPostDhcpAction() { - mCb.onPostDhcpAction(); - } - - // This is purely advisory and not an indication of provisioning - // success or failure. This is only here for callers that want to - // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress). - // DHCPv4 or static IPv4 configuration failure or success can be - // determined by whether or not the passed-in DhcpResults object is - // null or not. - @Override - public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) { - mCb.onNewDhcpResults(dhcpResults); - } - - @Override - public void onProvisioningSuccess(LinkProperties newLp) { - mCb.onProvisioningSuccess(newLp); - } - @Override - public void onProvisioningFailure(LinkProperties newLp) { - mCb.onProvisioningFailure(newLp); - } - - // Invoked on LinkProperties changes. - @Override - public void onLinkPropertiesChange(LinkProperties newLp) { - mCb.onLinkPropertiesChange(newLp); - } - - // Called when the internal IpReachabilityMonitor (if enabled) has - // detected the loss of a critical number of required neighbors. - @Override - public void onReachabilityLost(String logMsg) { - mCb.onReachabilityLost(logMsg); - } - - // Called when the IpClient state machine terminates. - @Override - public void onQuit() { - mCb.onQuit(); - } - - // Install an APF program to filter incoming packets. - @Override - public void installPacketFilter(byte[] filter) { - mCb.installPacketFilter(filter); - } - - // Asynchronously read back the APF program & data buffer from the wifi driver. - // Due to Wifi HAL limitations, the current implementation only supports dumping the entire - // buffer. In response to this request, the driver returns the data buffer asynchronously - // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message. - @Override - public void startReadPacketFilter() { - mCb.startReadPacketFilter(); - } - - // If multicast filtering cannot be accomplished with APF, this function will be called to - // actuate multicast filtering using another means. - @Override - public void setFallbackMulticastFilter(boolean enabled) { - mCb.setFallbackMulticastFilter(enabled); - } - - // Enabled/disable Neighbor Discover offload functionality. This is - // called, for example, whenever 464xlat is being started or stopped. - @Override - public void setNeighborDiscoveryOffload(boolean enable) { - mCb.setNeighborDiscoveryOffload(enable); - } - - // Invoked on starting preconnection process. - @Override - public void onPreconnectionStart(List<Layer2PacketParcelable> packets) { - mCb.onPreconnectionStart(packets); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - - @Override - public String getInterfaceHash() { - return this.HASH; - } - } - - /** - * Dump logs for the specified IpClient. - * TODO: remove callers and delete - */ - public static void dumpIpClient( - IIpClient connector, FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("IpClient logs have moved to dumpsys network_stack"); - } -} diff --git a/services/net/java/android/net/util/DhcpResultsCompatUtil.java b/services/net/java/android/net/util/DhcpResultsCompatUtil.java deleted file mode 100644 index fce0834c116e..000000000000 --- a/services/net/java/android/net/util/DhcpResultsCompatUtil.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import static android.net.shared.IpConfigurationParcelableUtil.unparcelAddress; - -import android.annotation.Nullable; -import android.net.DhcpResults; -import android.net.DhcpResultsParcelable; - -import java.net.Inet4Address; - -/** - * Compatibility utility for code that still uses DhcpResults. - * - * TODO: remove this class when all usages of DhcpResults (including Wifi in AOSP) are removed. - */ -public class DhcpResultsCompatUtil { - - /** - * Convert a DhcpResultsParcelable to DhcpResults. - * - * contract { - * returns(null) implies p == null - * returnsNotNull() implies p != null - * } - */ - @Nullable - public static DhcpResults fromStableParcelable(@Nullable DhcpResultsParcelable p) { - if (p == null) return null; - final DhcpResults results = new DhcpResults(p.baseConfiguration); - results.leaseDuration = p.leaseDuration; - results.mtu = p.mtu; - results.serverAddress = (Inet4Address) unparcelAddress(p.serverAddress); - results.vendorInfo = p.vendorInfo; - results.serverHostName = p.serverHostName; - results.captivePortalApiUrl = p.captivePortalApiUrl; - return results; - } -} diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java deleted file mode 100644 index 566698576026..000000000000 --- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.InvalidPacketException; -import android.net.KeepalivePacketData; -import android.net.NattKeepalivePacketData; -import android.net.NattKeepalivePacketDataParcelable; -import android.net.TcpKeepalivePacketData; -import android.net.TcpKeepalivePacketDataParcelable; -import android.os.Build; -import android.system.OsConstants; -import android.util.Log; - -import com.android.net.module.util.IpUtils; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Utility class to convert to/from keepalive data parcelables. - * - * TODO: move to networkstack-client library when it is moved to frameworks/libs/net. - * This class cannot go into other shared libraries as it depends on NetworkStack AIDLs. - * @hide - */ -public final class KeepalivePacketDataUtil { - private static final int IPV4_HEADER_LENGTH = 20; - private static final int IPV6_HEADER_LENGTH = 40; - private static final int TCP_HEADER_LENGTH = 20; - - private static final String TAG = KeepalivePacketDataUtil.class.getSimpleName(); - - /** - * Convert a NattKeepalivePacketData to a NattKeepalivePacketDataParcelable. - */ - @NonNull - public static NattKeepalivePacketDataParcelable toStableParcelable( - @NonNull NattKeepalivePacketData pkt) { - final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable(); - final InetAddress srcAddress = pkt.getSrcAddress(); - final InetAddress dstAddress = pkt.getDstAddress(); - parcel.srcAddress = srcAddress.getAddress(); - parcel.srcPort = pkt.getSrcPort(); - parcel.dstAddress = dstAddress.getAddress(); - parcel.dstPort = pkt.getDstPort(); - return parcel; - } - - /** - * Convert a TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable. - */ - @NonNull - public static TcpKeepalivePacketDataParcelable toStableParcelable( - @NonNull TcpKeepalivePacketData pkt) { - final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable(); - final InetAddress srcAddress = pkt.getSrcAddress(); - final InetAddress dstAddress = pkt.getDstAddress(); - parcel.srcAddress = srcAddress.getAddress(); - parcel.srcPort = pkt.getSrcPort(); - parcel.dstAddress = dstAddress.getAddress(); - parcel.dstPort = pkt.getDstPort(); - parcel.seq = pkt.getTcpSeq(); - parcel.ack = pkt.getTcpAck(); - parcel.rcvWnd = pkt.getTcpWindow(); - parcel.rcvWndScale = pkt.getTcpWindowScale(); - parcel.tos = pkt.getIpTos(); - parcel.ttl = pkt.getIpTtl(); - return parcel; - } - - /** - * Factory method to create tcp keepalive packet structure. - * @hide - */ - public static TcpKeepalivePacketData fromStableParcelable( - TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException { - final byte[] packet; - try { - if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null) - && (tcpDetails.srcAddress.length == 4 /* V4 IP length */) - && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) { - packet = buildV4Packet(tcpDetails); - } else { - // TODO: support ipv6 - throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); - } - return new TcpKeepalivePacketData( - InetAddress.getByAddress(tcpDetails.srcAddress), - tcpDetails.srcPort, - InetAddress.getByAddress(tcpDetails.dstAddress), - tcpDetails.dstPort, - packet, - tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale, - tcpDetails.tos, tcpDetails.ttl); - } catch (UnknownHostException e) { - throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); - } - - } - - /** - * Build ipv4 tcp keepalive packet, not including the link-layer header. - */ - // TODO : if this code is ever moved to the network stack, factorize constants with the ones - // over there. - private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) { - final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH; - ByteBuffer buf = ByteBuffer.allocate(length); - buf.order(ByteOrder.BIG_ENDIAN); - buf.put((byte) 0x45); // IP version and IHL - buf.put((byte) tcpDetails.tos); // TOS - buf.putShort((short) length); - buf.putInt(0x00004000); // ID, flags=DF, offset - buf.put((byte) tcpDetails.ttl); // TTL - buf.put((byte) OsConstants.IPPROTO_TCP); - final int ipChecksumOffset = buf.position(); - buf.putShort((short) 0); // IP checksum - buf.put(tcpDetails.srcAddress); - buf.put(tcpDetails.dstAddress); - buf.putShort((short) tcpDetails.srcPort); - buf.putShort((short) tcpDetails.dstPort); - buf.putInt(tcpDetails.seq); // Sequence Number - buf.putInt(tcpDetails.ack); // ACK - buf.putShort((short) 0x5010); // TCP length=5, flags=ACK - buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size - final int tcpChecksumOffset = buf.position(); - buf.putShort((short) 0); // TCP checksum - // URG is not set therefore the urgent pointer is zero. - buf.putShort((short) 0); // Urgent pointer - - buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0)); - buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum( - buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH)); - - return buf.array(); - } - - // TODO: add buildV6Packet. - - /** - * Get a {@link TcpKeepalivePacketDataParcelable} from {@link KeepalivePacketData}, if the - * generic class actually contains TCP keepalive data. - * - * @deprecated This method is used on R platforms where android.net.TcpKeepalivePacketData was - * not yet system API. Newer platforms should use android.net.TcpKeepalivePacketData directly. - * - * @param data A {@link KeepalivePacketData} that may contain TCP keepalive data. - * @return A parcelable containing TCP keepalive data, or null if the input data does not - * contain TCP keepalive data. - */ - @Deprecated - @SuppressWarnings("AndroidFrameworkCompatChange") // API version check used to Log.wtf - @Nullable - public static TcpKeepalivePacketDataParcelable parseTcpKeepalivePacketData( - @Nullable KeepalivePacketData data) { - if (data == null) return null; - - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { - Log.wtf(TAG, "parseTcpKeepalivePacketData should not be used after R, use " - + "TcpKeepalivePacketData instead."); - } - - // Reconstruct TcpKeepalivePacketData from the packet contained in KeepalivePacketData - final ByteBuffer buffer = ByteBuffer.wrap(data.getPacket()); - buffer.order(ByteOrder.BIG_ENDIAN); - - // Most of the fields are accessible from the KeepalivePacketData superclass: instead of - // using Struct to parse everything, just extract the extra fields necessary for - // TcpKeepalivePacketData. - final int tcpSeq; - final int tcpAck; - final int wndSize; - final int ipTos; - final int ttl; - try { - // This only support IPv4, because TcpKeepalivePacketData only supports IPv4 for R and - // below, and this method should not be used on newer platforms. - tcpSeq = buffer.getInt(IPV4_HEADER_LENGTH + 4); - tcpAck = buffer.getInt(IPV4_HEADER_LENGTH + 8); - wndSize = buffer.getShort(IPV4_HEADER_LENGTH + 14); - ipTos = buffer.get(1); - ttl = buffer.get(8); - } catch (IndexOutOfBoundsException e) { - return null; - } - - final TcpKeepalivePacketDataParcelable p = new TcpKeepalivePacketDataParcelable(); - p.srcAddress = data.getSrcAddress().getAddress(); - p.srcPort = data.getSrcPort(); - p.dstAddress = data.getDstAddress().getAddress(); - p.dstPort = data.getDstPort(); - p.seq = tcpSeq; - p.ack = tcpAck; - // TcpKeepalivePacketData could actually use non-zero wndScale, but this does not affect - // actual functionality as generated packets will be the same (no wndScale option added) - p.rcvWnd = wndSize; - p.rcvWndScale = 0; - p.tos = ipTos; - p.ttl = ttl; - return p; - } -} diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp new file mode 100644 index 000000000000..479ef8e5f188 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp @@ -0,0 +1,36 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "AppEnumerationInternalTests", + srcs: [ + "src/**/*.java", + ], + static_libs: [ + "compatibility-device-util-axt", + "androidx.test.runner", + "truth-prebuilt", + ], + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml index a2bbd2be777d..2039aaa20945 100644 --- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml +++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml @@ -12,8 +12,16 @@ ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and - ~ limitations under the License + ~ limitations under the License. --> -<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" - android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.appenumeration"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.pm.test.appenumeration" + android:label="Package Manager Service Tests for app enumeration"> + </instrumentation> + +</manifest> + diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml new file mode 100644 index 000000000000..6f168a3888b3 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<configuration description="Runs Package Manager Service App Enumeration Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="AppEnumerationInternalTests.apk" /> + </target_preparer> + + <!-- Create place to store apks --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="mkdir -p /data/local/tmp/appenumerationtests" /> + <option name="teardown-command" value="rm -rf /data/local/tmp/appenumerationtests"/> + </target_preparer> + + <!-- Load additional APKs onto device --> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" /> + </target_preparer> + + <option name="test-tag" value="AppEnumerationInternalTest" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.server.pm.test.appenumeration" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false" /> + </test> +</configuration> diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java new file mode 100644 index 000000000000..933784560410 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.test.appenumeration; + +import static com.android.compatibility.common.util.ShellUtils.runShellCommand; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.AppGlobals; +import android.content.pm.IPackageManager; +import android.content.pm.ProviderInfo; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Application enumeration tests for the internal apis of package manager service. + */ +@RunWith(AndroidJUnit4.class) +public class AppEnumerationInternalTests { + private static final String TEST_DATA_PATH = "/data/local/tmp/appenumerationtests/"; + private static final String SYNC_PROVIDER_APK_PATH = + TEST_DATA_PATH + "AppEnumerationSyncProviderTestApp.apk"; + private static final String SYNC_PROVIDER_PKG_NAME = "com.android.appenumeration.syncprovider"; + private static final String SYNC_PROVIDER_AUTHORITY = SYNC_PROVIDER_PKG_NAME; + + private IPackageManager mIPackageManager; + + @Before + public void setup() { + mIPackageManager = AppGlobals.getPackageManager(); + } + + @After + public void tearDown() throws Exception { + uninstallPackage(SYNC_PROVIDER_PKG_NAME); + } + + @Test + public void querySyncProviders_canSeeForceQueryable() throws Exception { + final List<String> names = new ArrayList<>(); + final List<ProviderInfo> infos = new ArrayList<>(); + installPackage(SYNC_PROVIDER_APK_PATH, true /* forceQueryable */); + mIPackageManager.querySyncProviders(names, infos); + + assertThat(names).contains(SYNC_PROVIDER_AUTHORITY); + assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList())) + .contains(SYNC_PROVIDER_PKG_NAME); + } + + @Test + public void querySyncProviders_cannotSeeSyncProvider() throws Exception { + final List<String> names = new ArrayList<>(); + final List<ProviderInfo> infos = new ArrayList<>(); + installPackage(SYNC_PROVIDER_APK_PATH, false /* forceQueryable */); + mIPackageManager.querySyncProviders(names, infos); + + assertThat(names).doesNotContain(SYNC_PROVIDER_AUTHORITY); + assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList())) + .doesNotContain(SYNC_PROVIDER_PKG_NAME); + } + + private static void installPackage(String apkPath, boolean forceQueryable) { + final StringBuilder cmd = new StringBuilder("pm install "); + if (forceQueryable) { + cmd.append("--force-queryable "); + } + cmd.append(apkPath); + final String result = runShellCommand(cmd.toString()); + assertThat(result.trim()).contains("Success"); + } + + private static void uninstallPackage(String packageName) { + runShellCommand("pm uninstall " + packageName); + } +} diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp new file mode 100644 index 000000000000..64239b4c6b2d --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp @@ -0,0 +1,36 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "AppEnumerationSyncProviderTestApp", + srcs: ["src/**/*.java"], + manifest: "AndroidManifest-syncprovider.xml", + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + test_suites: ["device-tests"], + platform_apis: true, +} diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-syncprovider.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-syncprovider.xml new file mode 100644 index 000000000000..de8439384b54 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-syncprovider.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.appenumeration.syncprovider"> + <application> + <provider android:name="com.android.appenumeration.testapp.DummyProvider" + android:authorities="com.android.appenumeration.syncprovider" + android:syncable="true" android:exported="true"> + <intent-filter> + <action android:name="com.android.appenumeration.action.PROVIDER"/> + </intent-filter> + </provider> + </application> +</manifest> diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/src/com/android/appenumeration/testapp/DummyProvider.java b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/src/com/android/appenumeration/testapp/DummyProvider.java new file mode 100644 index 000000000000..e8b610969c2f --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/src/com/android/appenumeration/testapp/DummyProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.appenumeration.testapp; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class DummyProvider extends ContentProvider { + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public String getType(Uri uri) { + return "text/plain"; + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 21de7916e23d..eab1afbd931e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -1914,11 +1914,11 @@ public class AlarmManagerServiceTest { public void hasScheduleExactAlarmBinderCallChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); - mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); - assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED); - assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } private void mockChangeEnabled(long changeId, boolean enabled) { @@ -2309,12 +2309,14 @@ public class AlarmManagerServiceTest { public void minWindowChangeDisabled() { mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, false); final long minWindow = 73; + final long futurity = 10_000; setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC. for (int window = 1; window <= minWindow; window++) { final PendingIntent pi = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0, + TEST_CALLING_UID, null); assertEquals(1, mService.mAlarmStore.size()); final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0); @@ -2323,18 +2325,61 @@ public class AlarmManagerServiceTest { } @Test + public void minWindowExempted() { + mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true); + final long minWindow = 73; + final long futurity = 10_000; + + setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); + + final int coreUid = 2312; + doReturn(true).when(() -> UserHandle.isCore(coreUid)); + + final int allowlisted = 54239; + when(mDeviceIdleInternal.isAppOnWhitelist(UserHandle.getAppId(allowlisted))).thenReturn( + true); + + for (final int callingUid : new int[]{SYSTEM_UI_UID, coreUid, coreUid}) { + // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC. + for (int window = 1; window <= minWindow; window++) { + final PendingIntent pi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0, + callingUid, null); + + assertEquals(1, mService.mAlarmStore.size()); + final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0); + assertEquals(window, a.windowLength); + } + } + + // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC. + for (int window = 1; window <= minWindow; window++) { + final PendingIntent pi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0, + TEST_CALLING_UID, null); + + assertEquals(1, mService.mAlarmStore.size()); + final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0); + assertEquals(minWindow, a.windowLength); + } + } + + @Test public void minWindowPriorityAlarm() { mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true); final long minWindow = 73; + final long futurity = 10_000; setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC. for (int window = 1; window <= minWindow; window++) { - setPrioritizedAlarm(ELAPSED_REALTIME, 0, window, new IAlarmListener.Stub() { - @Override - public void doAlarm(IAlarmCompleteListener callback) throws RemoteException { - } - }); + setPrioritizedAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, + new IAlarmListener.Stub() { + @Override + public void doAlarm(IAlarmCompleteListener callback) + throws RemoteException { + } + }); assertEquals(1, mService.mAlarmStore.size()); final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0); assertEquals(window, a.windowLength); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index e1012a9a4cad..0efcc57eeec2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -589,8 +590,8 @@ public class LocalDisplayAdapterTest { new DisplayModeDirector.DesiredDisplayModeSpecs( /*baseModeId*/ baseModeId, /*allowGroupSwitching*/ false, - new DisplayModeDirector.RefreshRateRange(60f, 60f), - new DisplayModeDirector.RefreshRateRange(60f, 60f) + new RefreshRateRange(60f, 60f), + new RefreshRateRange(60f, 60f) )); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, @@ -624,8 +625,8 @@ public class LocalDisplayAdapterTest { new DisplayModeDirector.DesiredDisplayModeSpecs( /*baseModeId*/ baseModeId, /*allowGroupSwitching*/ false, - new DisplayModeDirector.RefreshRateRange(60f, 60f), - new DisplayModeDirector.RefreshRateRange(60f, 60f) + new RefreshRateRange(60f, 60f), + new RefreshRateRange(60f, 60f) )); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java index cf5db2e0db98..8532dbb7f38a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java @@ -158,7 +158,7 @@ public class MockableLocationProviderTest { @Test public void testSetState() { - assertThat(mProvider.isAllowed()).isFalse(); + assertThat(mProvider.getState().allowed).isFalse(); AbstractLocationProvider.State newState; diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java index 5a42c4bc7f3a..53483f6d70bc 100644 --- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java @@ -31,6 +31,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.junit.Assume.assumeThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -57,6 +58,9 @@ import android.service.wallpaper.WallpaperService; import android.testing.TestableContext; import android.util.Log; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import android.util.Xml; import android.view.Display; import androidx.test.filters.FlakyTest; @@ -82,9 +86,12 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.quality.Strictness; +import org.xmlpull.v1.XmlPullParserException; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; /** * Tests for the {@link WallpaperManagerService} class. @@ -355,6 +362,31 @@ public class WallpaperManagerServiceTests { verifyDisplayData(); } + @Test + public void testXmlSerializationRoundtrip() { + WallpaperData systemWallpaperData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0); + try { + TypedXmlSerializer serializer = Xml.newBinarySerializer(); + serializer.setOutput(new ByteArrayOutputStream(), StandardCharsets.UTF_8.name()); + serializer.startDocument(StandardCharsets.UTF_8.name(), true); + mService.writeWallpaperAttributes(serializer, "wp", systemWallpaperData); + } catch (IOException e) { + fail("exception occurred while writing system wallpaper attributes"); + } + + WallpaperData shouldMatchSystem = new WallpaperData(systemWallpaperData.userId, + systemWallpaperData.wallpaperFile.getParentFile(), + systemWallpaperData.wallpaperFile.getAbsolutePath(), + systemWallpaperData.cropFile.getAbsolutePath()); + try { + TypedXmlPullParser parser = Xml.newBinaryPullParser(); + mService.parseWallpaperAttributes(parser, shouldMatchSystem, true); + } catch (XmlPullParserException e) { + fail("exception occurred while parsing wallpaper"); + } + assertEquals(systemWallpaperData.primaryColors, shouldMatchSystem.primaryColors); + } + // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for // non-current user must not bind to wallpaper service. private void verifyNoConnectionBeforeLastUser(int lastUserId) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java index 1ac4a8ed96d0..a71b481372d8 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java @@ -19,6 +19,7 @@ package com.android.server.accessibility; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_HOVER_MOVE; import static android.view.MotionEvent.ACTION_UP; +import static android.view.WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY; import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER; import static org.hamcrest.CoreMatchers.allOf; @@ -186,9 +187,9 @@ public class MotionEventInjectorTest { verifyNoMoreInteractions(next); mMessageCapturingHandler.sendOneMessage(); // Send a motion event - verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), eq(FLAG_PASS_TO_USER)); - verify(next).onMotionEvent(argThat(mIsLineStart), argThat(mIsLineStart), - eq(FLAG_PASS_TO_USER)); + final int expectedFlags = FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY; + verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), eq(expectedFlags)); + verify(next).onMotionEvent(argThat(mIsLineStart), argThat(mIsLineStart), eq(expectedFlags)); verifyNoMoreInteractions(next); reset(next); @@ -196,7 +197,7 @@ public class MotionEventInjectorTest { mMessageCapturingHandler.sendOneMessage(); // Send a motion event verify(next).onMotionEvent(argThat(allOf(mIsLineMiddle, hasRightDownTime)), - argThat(allOf(mIsLineMiddle, hasRightDownTime)), eq(FLAG_PASS_TO_USER)); + argThat(allOf(mIsLineMiddle, hasRightDownTime)), eq(expectedFlags)); verifyNoMoreInteractions(next); reset(next); @@ -204,7 +205,7 @@ public class MotionEventInjectorTest { mMessageCapturingHandler.sendOneMessage(); // Send a motion event verify(next).onMotionEvent(argThat(allOf(mIsLineEnd, hasRightDownTime)), - argThat(allOf(mIsLineEnd, hasRightDownTime)), eq(FLAG_PASS_TO_USER)); + argThat(allOf(mIsLineEnd, hasRightDownTime)), eq(expectedFlags)); verifyNoMoreInteractions(next); verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, true); @@ -242,7 +243,8 @@ public class MotionEventInjectorTest { mMessageCapturingHandler.sendAllMessages(); // Send all motion events reset(next); mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); - verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown), eq(0)); + verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown), + eq(FLAG_INJECTED_FROM_ACCESSIBILITY)); } @Test @@ -258,7 +260,8 @@ public class MotionEventInjectorTest { mMessageCapturingHandler.sendOneMessage(); // Send a motion event verify(next).onMotionEvent( - argThat(mIsLineStart), argThat(mIsLineStart), eq(FLAG_PASS_TO_USER)); + argThat(mIsLineStart), argThat(mIsLineStart), + eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY)); } @Test @@ -289,9 +292,11 @@ public class MotionEventInjectorTest { reset(next); mMessageCapturingHandler.sendOneMessage(); // Send a motion event - verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), eq(FLAG_PASS_TO_USER)); + verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), + eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY)); verify(next).onMotionEvent( - argThat(mIsLineStart), argThat(mIsLineStart), eq(FLAG_PASS_TO_USER)); + argThat(mIsLineStart), argThat(mIsLineStart), + eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java index 3bcbcbdbd2c8..aadaba4d2fce 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java @@ -42,6 +42,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.compatibility.common.util.SystemUtil; import com.android.server.appsearch.external.localstorage.util.PrefixUtil; +import com.android.server.appsearch.visibilitystore.VisibilityStore; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -57,10 +58,16 @@ import java.util.Map; /** This tests AppSearchImpl when it's running with a platform-backed VisibilityStore. */ public class AppSearchImplPlatformTest { + /** + * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class. + */ + private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true; + @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>(); private Context mContext; private AppSearchImpl mAppSearchImpl; + private VisibilityStore mVisibilityStore; private int mGlobalQuerierUid; @Before @@ -84,12 +91,9 @@ public class AppSearchImplPlatformTest { }; // Give ourselves global query permissions - mAppSearchImpl = - AppSearchImpl.create( - mTemporaryFolder.newFolder(), - mContext, - /*logger=*/ null); - + mAppSearchImpl = AppSearchImpl.create( + mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); + mVisibilityStore = VisibilityStore.create(mAppSearchImpl, mContext); mGlobalQuerierUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0); } @@ -122,6 +126,7 @@ public class AppSearchImplPlatformTest { "package", "database", Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), + mVisibilityStore, /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), /*schemasPackageAccessible=*/ ImmutableMap.of( "schema1", @@ -130,26 +135,20 @@ public class AppSearchImplPlatformTest { /*schemaVersion=*/ 0); // "schema1" is platform hidden now and package visible to package1 - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mContext.getPackageName(), - mGlobalQuerierUid)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + mGlobalQuerierUid, + /*callerHasSystemAccess=*/ true)) .isFalse(); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - packageNameFoo, - uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isTrue(); // Add a new schema, and include the already-existing "schema1" @@ -159,6 +158,7 @@ public class AppSearchImplPlatformTest { ImmutableList.of( new AppSearchSchema.Builder("schema1").build(), new AppSearchSchema.Builder("schema2").build()), + mVisibilityStore, /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), /*schemasPackageAccessible=*/ ImmutableMap.of( "schema1", @@ -168,50 +168,40 @@ public class AppSearchImplPlatformTest { // Check that "schema1" still has the same visibility settings SystemUtil.runWithShellPermissionIdentity(() -> assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mContext.getPackageName(), - mGlobalQuerierUid)) + mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + mGlobalQuerierUid, + /*callerHasSystemAccess=*/ true)) .isFalse(), READ_GLOBAL_APP_SEARCH_DATA); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - packageNameFoo, - uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isTrue(); // "schema2" has default visibility settings SystemUtil.runWithShellPermissionIdentity(() -> assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema2", - mContext.getPackageName(), - mGlobalQuerierUid)) + mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema2", + mGlobalQuerierUid, + /*callerHasSystemAccess=*/ true)) .isTrue(), READ_GLOBAL_APP_SEARCH_DATA); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema2", - packageNameFoo, - uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema2", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isFalse(); } @@ -242,6 +232,7 @@ public class AppSearchImplPlatformTest { "package", "database", Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), + mVisibilityStore, /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), /*schemasPackageAccessible=*/ ImmutableMap.of( "schema1", @@ -250,26 +241,20 @@ public class AppSearchImplPlatformTest { /*schemaVersion=*/ 0); // "schema1" is platform hidden now and package accessible - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mContext.getPackageName(), - mGlobalQuerierUid)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + mGlobalQuerierUid, + /*callerHasSystemAccess=*/ true)) .isFalse(); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - packageNameFoo, - uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isTrue(); // Remove "schema1" by force overriding @@ -277,32 +262,27 @@ public class AppSearchImplPlatformTest { "package", "database", /*schemas=*/ Collections.emptyList(), + mVisibilityStore, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ true, /*schemaVersion=*/ 0); // Check that "schema1" is no longer considered platform hidden or package accessible - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mContext.getPackageName(), - mGlobalQuerierUid)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + mGlobalQuerierUid, + /*callerHasSystemAccess=*/ true)) .isTrue(); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - packageNameFoo, - uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isFalse(); // Add "schema1" back, it gets default visibility settings which means it's not platform @@ -311,30 +291,25 @@ public class AppSearchImplPlatformTest { "package", "database", Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), + mVisibilityStore, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*schemaVersion=*/ 0); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mContext.getPackageName(), - mGlobalQuerierUid)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + mGlobalQuerierUid, + /*callerHasSystemAccess=*/ true)) .isTrue(); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - packageNameFoo, - uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "schema1", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isFalse(); } @@ -351,20 +326,18 @@ public class AppSearchImplPlatformTest { "package", "database", Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + mVisibilityStore, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*schemaVersion=*/ 0); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "Schema", - mContext.getPackageName(), - mGlobalQuerierUid)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "Schema", + mGlobalQuerierUid, + /*callerHasSystemAccess=*/ true)) .isTrue(); } @@ -381,20 +354,18 @@ public class AppSearchImplPlatformTest { "package", "database", Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + mVisibilityStore, /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*schemaVersion=*/ 0); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "Schema", - mContext.getPackageName(), - mGlobalQuerierUid)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + prefix + "Schema", + mGlobalQuerierUid, + /*callerHasSystemAccess=*/ true)) .isFalse(); } @@ -412,19 +383,18 @@ public class AppSearchImplPlatformTest { "package", "database", Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + mVisibilityStore, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*schemaVersion=*/ 0); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() + assertThat(mVisibilityStore .isSchemaSearchableByCaller( "package", "database", prefix + "Schema", - packageName, - /*callerUid=*/ 42)) + /*callerUid=*/ 42, + /*callerHasSystemAccess=*/ false)) .isFalse(); } @@ -452,21 +422,20 @@ public class AppSearchImplPlatformTest { "package", "database", Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + mVisibilityStore, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ ImmutableMap.of( "Schema", ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))), /*forceOverride=*/ false, /*schemaVersion=*/ 0); - assertThat( - mAppSearchImpl - .getVisibilityStoreLocked() + assertThat(mVisibilityStore .isSchemaSearchableByCaller( "package", "database", prefix + "Schema", - packageNameFoo, - uidFoo)) + uidFoo, + /*callerHasSystemAccess=*/ false)) .isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java index 6ac4d138c9d1..b67ebe423eab 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java @@ -56,6 +56,11 @@ import java.util.Collections; import java.util.Map; public class VisibilityStoreTest { + /** + * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class. + */ + private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true; + @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>(); private Context mContext; @@ -83,14 +88,10 @@ public class VisibilityStoreTest { }; // Give ourselves global query permissions - AppSearchImpl appSearchImpl = - AppSearchImpl.create( - mTemporaryFolder.newFolder(), - mContext, - /*logger=*/ null); - + AppSearchImpl appSearchImpl = AppSearchImpl.create( + mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); + mVisibilityStore = VisibilityStore.create(appSearchImpl, mContext); mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0); - mVisibilityStore = appSearchImpl.getVisibilityStoreLocked(); } /** @@ -116,12 +117,28 @@ public class VisibilityStoreTest { } @Test + public void testDoesCallerHaveSystemAccess() { + PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); + when(mockPackageManager + .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) + .thenReturn(PERMISSION_GRANTED); + assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue(); + + when(mockPackageManager + .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) + .thenReturn(PERMISSION_DENIED); + assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())) + .isFalse(); + } + + @Test public void testSetVisibility_platformSurfaceable() throws Exception { // Make sure we have global query privileges PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); when(mockPackageManager .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) .thenReturn(PERMISSION_GRANTED); + assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue(); mVisibilityStore.setVisibility( "package", @@ -134,16 +151,16 @@ public class VisibilityStoreTest { "package", "database", "prefix/schema1", - mContext.getPackageName(), - mUid)) + mUid, + /*callerHasSystemAccess=*/ true)) .isFalse(); assertThat( mVisibilityStore.isSchemaSearchableByCaller( "package", "database", "prefix/schema2", - mContext.getPackageName(), - mUid)) + mUid, + /*callerHasSystemAccess=*/ true)) .isFalse(); // New .setVisibility() call completely overrides previous visibility settings. @@ -159,24 +176,24 @@ public class VisibilityStoreTest { "package", "database", "prefix/schema1", - mContext.getPackageName(), - mUid)) + mUid, + /*callerHasSystemAccess=*/ true)) .isFalse(); assertThat( mVisibilityStore.isSchemaSearchableByCaller( "package", "database", "prefix/schema2", - mContext.getPackageName(), - mUid)) + mUid, + /*callerHasSystemAccess=*/ true)) .isTrue(); assertThat( mVisibilityStore.isSchemaSearchableByCaller( "package", "database", "prefix/schema3", - mContext.getPackageName(), - mUid)) + mUid, + /*callerHasSystemAccess=*/ true)) .isFalse(); // Everything defaults to visible again. @@ -190,24 +207,24 @@ public class VisibilityStoreTest { "package", "database", "prefix/schema1", - mContext.getPackageName(), - mUid)) + mUid, + /*callerHasSystemAccess=*/ true)) .isTrue(); assertThat( mVisibilityStore.isSchemaSearchableByCaller( "package", "database", "prefix/schema2", - mContext.getPackageName(), - mUid)) + mUid, + /*callerHasSystemAccess=*/ true)) .isTrue(); assertThat( mVisibilityStore.isSchemaSearchableByCaller( "package", "database", "prefix/schema3", - mContext.getPackageName(), - mUid)) + mUid, + /*callerHasSystemAccess=*/ true)) .isTrue(); } @@ -236,13 +253,19 @@ public class VisibilityStoreTest { .thenReturn(PERMISSION_DENIED); // By default, a schema isn't package accessible. - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + "prefix/schemaFoo", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isFalse(); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", "database", "prefix/schemaBar", packageNameBar, uidBar)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + "prefix/schemaBar", + uidBar, + /*callerHasSystemAccess=*/ false)) .isFalse(); // Grant package access @@ -262,9 +285,12 @@ public class VisibilityStoreTest { when(mockPackageManager.hasSigningCertificate( packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) .thenReturn(false); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + "prefix/schemaFoo", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isFalse(); // Should fail if PackageManager doesn't think the package belongs to the uid @@ -273,9 +299,12 @@ public class VisibilityStoreTest { when(mockPackageManager.hasSigningCertificate( packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) .thenReturn(true); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + "prefix/schemaFoo", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isFalse(); // But if uid and certificate match, then we should have access @@ -284,9 +313,12 @@ public class VisibilityStoreTest { when(mockPackageManager.hasSigningCertificate( packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) .thenReturn(true); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + "prefix/schemaFoo", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isTrue(); when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt())) @@ -294,9 +326,12 @@ public class VisibilityStoreTest { when(mockPackageManager.hasSigningCertificate( packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256)) .thenReturn(true); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", "database", "prefix/schemaBar", packageNameBar, uidBar)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + "prefix/schemaBar", + uidBar, + /*callerHasSystemAccess=*/ false)) .isTrue(); // New .setVisibility() call completely overrides previous visibility settings. So @@ -314,9 +349,12 @@ public class VisibilityStoreTest { when(mockPackageManager.hasSigningCertificate( packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) .thenReturn(true); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + "prefix/schemaFoo", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isTrue(); when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt())) @@ -324,9 +362,12 @@ public class VisibilityStoreTest { when(mockPackageManager.hasSigningCertificate( packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256)) .thenReturn(true); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", "database", "prefix/schemaBar", packageNameBar, uidBar)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + "prefix/schemaBar", + uidBar, + /*callerHasSystemAccess=*/ false)) .isFalse(); } @@ -357,9 +398,12 @@ public class VisibilityStoreTest { ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)))); // If we can't verify the Foo package that has access, assume it doesn't have access. - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo)) + assertThat(mVisibilityStore.isSchemaSearchableByCaller( + "package", + "database", + "prefix/schemaFoo", + uidFoo, + /*callerHasSystemAccess=*/ false)) .isFalse(); } @@ -391,8 +435,8 @@ public class VisibilityStoreTest { /*packageName=*/ "", /*databaseName=*/ "", "schema", - mContext.getPackageName(), - mUid)) + mUid, + /*callerHasSystemAccess=*/ true)) .isTrue(); when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) @@ -405,8 +449,8 @@ public class VisibilityStoreTest { /*packageName=*/ "", /*databaseName=*/ "", "schema", - packageNameFoo, - uidFoo)) + uidFoo, + /*callerHasSystemAccess=*/ false)) .isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java index 5a8c44caa0a3..f032402f47a0 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java @@ -76,17 +76,16 @@ import java.util.Set; public class AppSearchImplTest { @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private AppSearchImpl mAppSearchImpl; + /** + * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class. + */ + private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true; @Before public void setUp() throws Exception { - Context context = ApplicationProvider.getApplicationContext(); - - // Give ourselves global query permissions mAppSearchImpl = AppSearchImpl.create( - mTemporaryFolder.newFolder(), - context, - /*logger=*/ null); + mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); } /** @@ -144,7 +143,7 @@ public class AppSearchImplTest { .build(); AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = - mAppSearchImpl.rewriteSchema( + AppSearchImpl.rewriteSchema( createPrefix("package", "newDatabase"), existingSchemaBuilder, newSchema); // We rewrote all the new types that were added. And nothing was removed. @@ -244,7 +243,7 @@ public class AppSearchImplTest { .build(); AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = - mAppSearchImpl.rewriteSchema( + AppSearchImpl.rewriteSchema( createPrefix("package", "existingDatabase"), existingSchemaBuilder, newSchema); @@ -279,7 +278,7 @@ public class AppSearchImplTest { .build(); AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = - mAppSearchImpl.rewriteSchema( + AppSearchImpl.rewriteSchema( createPrefix("package", "existingDatabase"), existingSchemaBuilder, newSchema); @@ -381,7 +380,7 @@ public class AppSearchImplTest { } @Test - public void testRemoveDatabasesFromDocumentThrowsException() throws Exception { + public void testRemoveDatabasesFromDocumentThrowsException() { // Set two different database names in the document, which should never happen DocumentProto documentProto = DocumentProto.newBuilder() @@ -398,7 +397,7 @@ public class AppSearchImplTest { } @Test - public void testNestedRemoveDatabasesFromDocumentThrowsException() throws Exception { + public void testNestedRemoveDatabasesFromDocumentThrowsException() { // Set two different database names in the outer and inner document, which should never // happen. DocumentProto insideDocument = @@ -423,7 +422,7 @@ public class AppSearchImplTest { } @Test - public void testOptimize() throws Exception { + public void testTriggerCheckOptimizeByMutationSize() throws Exception { // Insert schema List<AppSearchSchema> schemas = Collections.singletonList(new AppSearchSchema.Builder("type").build()); @@ -431,61 +430,36 @@ public class AppSearchImplTest { "package", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); - // Insert enough documents. - for (int i = 0; - i - < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; - i++) { - GenericDocument document = - new GenericDocument.Builder<>("namespace", "id" + i, "type").build(); - mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); - } + // Insert a document and then remove it to generate garbage. + GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build(); + mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); + mAppSearchImpl.remove( + "package", "database", "namespace", "id", /*removeStatsBuilder=*/ null); - // Check optimize() will release 0 docs since there is no deletion. + // Verify there is garbage documents. GetOptimizeInfoResultProto optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked(); - assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(0); - - // delete 999 documents, we will reach the threshold to trigger optimize() in next - // deletion. - for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1; i++) { - mAppSearchImpl.remove( - "package", "database", "namespace", "id" + i, /*removeStatsBuilder=*/ null); - } + assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(1); - // Updates the check for optimize counter, checkForOptimize() will be triggered since - // CHECK_OPTIMIZE_INTERVAL is reached but optimize() won't since - // OPTIMIZE_THRESHOLD_DOC_COUNT is not. - mAppSearchImpl.checkForOptimize( - /*mutateBatchSize=*/ AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1); + // Increase mutation counter and stop before reach the threshold + mAppSearchImpl.checkForOptimize(AppSearchImpl.CHECK_OPTIMIZE_INTERVAL - 1); - // Verify optimize() still not be triggered. + // Verify the optimize() isn't triggered. optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked(); - assertThat(optimizeInfo.getOptimizableDocs()) - .isEqualTo(AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1); - - // Keep delete docs - for (int i = AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT; - i - < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; - i++) { - mAppSearchImpl.remove( - "package", "database", "namespace", "id" + i, /*removeStatsBuilder=*/ null); - } - // updates the check for optimize counter, will reach both CHECK_OPTIMIZE_INTERVAL and - // OPTIMIZE_THRESHOLD_DOC_COUNT this time and trigger a optimize(). - mAppSearchImpl.checkForOptimize(/*mutateBatchSize*/ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL); + assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(1); + + // Increase the counter and reach the threshold, optimize() should be triggered. + mAppSearchImpl.checkForOptimize(/*mutateBatchSize=*/ 1); - // Verify optimize() is triggered + // Verify optimize() is triggered. optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked(); - assertThat(optimizeInfo.getOptimizableDocs()) - .isLessThan(AppSearchImpl.CHECK_OPTIMIZE_INTERVAL); + assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(0); + assertThat(optimizeInfo.getEstimatedOptimizableBytes()).isEqualTo(0); } @Test @@ -493,7 +467,8 @@ public class AppSearchImplTest { // Setup the index Context context = ApplicationProvider.getApplicationContext(); File appsearchDir = mTemporaryFolder.newFolder(); - AppSearchImpl appSearchImpl = AppSearchImpl.create(appsearchDir, context, /*logger=*/ null); + AppSearchImpl appSearchImpl = + AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); // Insert schema List<AppSearchSchema> schemas = @@ -504,6 +479,7 @@ public class AppSearchImplTest { context.getPackageName(), "database1", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -522,7 +498,9 @@ public class AppSearchImplTest { /*queryExpression=*/ "", new SearchSpec.Builder().addFilterSchemas("Type1").build(), context.getPackageName(), - VisibilityStore.NO_OP_USER_ID, + /*visibilityStore=*/ null, + VisibilityStore.NO_OP_UID, + /*callerHasSystemAccess=*/ false, /*logger=*/ null); assertThat(results.getResults()).hasSize(1); assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc); @@ -548,16 +526,13 @@ public class AppSearchImplTest { PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc); assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK); - // Create a logger for capturing initialization to make sure we are logging the recovery - // process correctly. - AppSearchLoggerTest.TestLogger testLogger = new AppSearchLoggerTest.TestLogger(); - // Initialize AppSearchImpl. This should cause a reset. + InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); appSearchImpl.close(); - appSearchImpl = AppSearchImpl.create(appsearchDir, context, testLogger); + appSearchImpl = AppSearchImpl.create(appsearchDir, initStatsBuilder, ALWAYS_OPTIMIZE); // Check recovery state - InitializeStats initStats = testLogger.mInitializeStats; + InitializeStats initStats = initStatsBuilder.build(); assertThat(initStats).isNotNull(); assertThat(initStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR); assertThat(initStats.hasDeSync()).isFalse(); @@ -582,7 +557,9 @@ public class AppSearchImplTest { /*queryExpression=*/ "", new SearchSpec.Builder().addFilterSchemas("Type1").build(), context.getPackageName(), - VisibilityStore.NO_OP_USER_ID, + /*visibilityStore=*/ null, + VisibilityStore.NO_OP_UID, + /*callerHasSystemAccess=*/ false, /*logger=*/ null); assertThat(results.getResults()).isEmpty(); @@ -591,6 +568,7 @@ public class AppSearchImplTest { context.getPackageName(), "database1", Collections.singletonList(new AppSearchSchema.Builder("Type1").build()), + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -606,7 +584,9 @@ public class AppSearchImplTest { /*queryExpression=*/ "", new SearchSpec.Builder().addFilterSchemas("Type1").build(), context.getPackageName(), - VisibilityStore.NO_OP_USER_ID, + /*visibilityStore=*/ null, + VisibilityStore.NO_OP_UID, + /*callerHasSystemAccess=*/ false, /*logger=*/ null); assertThat(results.getResults()).hasSize(1); assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc); @@ -623,6 +603,7 @@ public class AppSearchImplTest { "package", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -656,6 +637,7 @@ public class AppSearchImplTest { "package", "database1", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -664,6 +646,7 @@ public class AppSearchImplTest { "package", "database2", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -708,6 +691,7 @@ public class AppSearchImplTest { "package", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -749,6 +733,7 @@ public class AppSearchImplTest { "package1", "database1", schema1, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -761,6 +746,7 @@ public class AppSearchImplTest { "package2", "database2", schema2, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -803,6 +789,7 @@ public class AppSearchImplTest { "package1", "database1", schema1, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -815,6 +802,7 @@ public class AppSearchImplTest { "package2", "database2", schema2, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -862,7 +850,9 @@ public class AppSearchImplTest { "", searchSpec, /*callerPackageName=*/ "", - /*callerUid=*/ 0, + /*visibilityStore=*/ null, + VisibilityStore.NO_OP_UID, + /*callerHasSystemAccess=*/ false, /*logger=*/ null); assertThat(searchResultPage.getResults()).isEmpty(); } @@ -902,6 +892,7 @@ public class AppSearchImplTest { "package", "database1", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -925,9 +916,6 @@ public class AppSearchImplTest { @Test public void testSetSchema_incompatible() throws Exception { - List<SchemaTypeConfigProto> existingSchemas = - mAppSearchImpl.getSchemaProtoLocked().getTypesList(); - List<AppSearchSchema> oldSchemas = new ArrayList<>(); oldSchemas.add( new AppSearchSchema.Builder("Email") @@ -949,6 +937,7 @@ public class AppSearchImplTest { "package", "database1", oldSchemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -964,6 +953,7 @@ public class AppSearchImplTest { "package", "database1", newSchemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ true, @@ -986,6 +976,7 @@ public class AppSearchImplTest { "package", "database1", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1018,6 +1009,7 @@ public class AppSearchImplTest { "package", "database1", finalSchemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1031,6 +1023,7 @@ public class AppSearchImplTest { "package", "database1", finalSchemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ true, @@ -1068,6 +1061,7 @@ public class AppSearchImplTest { "package", "database1", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1076,6 +1070,7 @@ public class AppSearchImplTest { "package", "database2", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1115,6 +1110,7 @@ public class AppSearchImplTest { "package", "database1", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ true, @@ -1159,6 +1155,7 @@ public class AppSearchImplTest { "package", "database", schema, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1220,6 +1217,7 @@ public class AppSearchImplTest { "packageA", "database", schema, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1228,6 +1226,7 @@ public class AppSearchImplTest { "packageB", "database", schema, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1273,6 +1272,7 @@ public class AppSearchImplTest { "package1", "database1", Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1286,6 +1286,7 @@ public class AppSearchImplTest { "package1", "database2", Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1299,6 +1300,7 @@ public class AppSearchImplTest { "package2", "database1", Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1357,6 +1359,7 @@ public class AppSearchImplTest { "package", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1507,6 +1510,7 @@ public class AppSearchImplTest { "package1", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1529,6 +1533,7 @@ public class AppSearchImplTest { "package1", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1544,6 +1549,7 @@ public class AppSearchImplTest { "package2", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1592,6 +1598,7 @@ public class AppSearchImplTest { "package1", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1614,6 +1621,7 @@ public class AppSearchImplTest { "package1", "database1", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1635,6 +1643,7 @@ public class AppSearchImplTest { "package1", "database1", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1643,6 +1652,7 @@ public class AppSearchImplTest { "package1", "database2", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1678,12 +1688,9 @@ public class AppSearchImplTest { @Test public void testThrowsExceptionIfClosed() throws Exception { - Context context = ApplicationProvider.getApplicationContext(); AppSearchImpl appSearchImpl = AppSearchImpl.create( - mTemporaryFolder.newFolder(), - context, - /*logger=*/ null); + mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); // Initial check that we could do something at first. List<AppSearchSchema> schemas = @@ -1692,6 +1699,7 @@ public class AppSearchImplTest { "package", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1702,135 +1710,115 @@ public class AppSearchImplTest { // Check all our public APIs expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.setSchema( - "package", - "database", - schemas, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - }); + () -> + appSearchImpl.setSchema( + "package", + "database", + schemas, + /*visibilityStore=*/ null, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false, + /*version=*/ 0)); expectThrows( - IllegalStateException.class, - () -> { - appSearchImpl.getSchema("package", "database"); - }); + IllegalStateException.class, () -> appSearchImpl.getSchema("package", "database")); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.putDocument( - "package", - "database", - new GenericDocument.Builder<>("namespace", "id", "type").build(), - /*logger=*/ null); - }); + () -> + appSearchImpl.putDocument( + "package", + "database", + new GenericDocument.Builder<>("namespace", "id", "type").build(), + /*logger=*/ null)); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.getDocument( - "package", "database", "namespace", "id", Collections.emptyMap()); - }); + () -> + appSearchImpl.getDocument( + "package", "database", "namespace", "id", Collections.emptyMap())); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.query( - "package", - "database", - "query", - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .build(), - /*logger=*/ null); - }); + () -> + appSearchImpl.query( + "package", + "database", + "query", + new SearchSpec.Builder().build(), + /*logger=*/ null)); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.globalQuery( - "query", - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .build(), - "package", - /*callerUid=*/ 1, - /*logger=*/ null); - }); + () -> + appSearchImpl.globalQuery( + "query", + new SearchSpec.Builder().build(), + "package", + /*visibilityStore=*/ null, + VisibilityStore.NO_OP_UID, + /*callerHasSystemAccess=*/ false, + /*logger=*/ null)); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.getNextPage(/*nextPageToken=*/ 1L); - }); + () -> appSearchImpl.getNextPage(/*nextPageToken=*/ 1L)); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L); - }); + () -> appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L)); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.reportUsage( - "package", - "database", - "namespace", - "id", - /*usageTimestampMillis=*/ 1000L, - /*systemUsage=*/ false); - }); + () -> + appSearchImpl.reportUsage( + "package", + "database", + "namespace", + "id", + /*usageTimestampMillis=*/ 1000L, + /*systemUsage=*/ false)); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.remove( - "package", "database", "namespace", "id", /*statsBuilder=*/ null); - }); + () -> + appSearchImpl.remove( + "package", + "database", + "namespace", + "id", + /*removeStatsBuilder=*/ null)); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.removeByQuery( - "package", - "database", - "query", - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .build(), - /*statsBuilder=*/ null); - }); + () -> + appSearchImpl.removeByQuery( + "package", + "database", + "query", + new SearchSpec.Builder().build(), + /*removeStatsBuilder=*/ null)); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.getStorageInfoForPackage("package"); - }); + () -> appSearchImpl.getStorageInfoForPackage("package")); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.getStorageInfoForDatabase("package", "database"); - }); + () -> appSearchImpl.getStorageInfoForDatabase("package", "database")); expectThrows( IllegalStateException.class, - () -> { - appSearchImpl.persistToDisk(PersistType.Code.FULL); - }); + () -> appSearchImpl.persistToDisk(PersistType.Code.FULL)); } @Test public void testPutPersistsWithLiteFlush() throws Exception { // Setup the index - Context context = ApplicationProvider.getApplicationContext(); File appsearchDir = mTemporaryFolder.newFolder(); - AppSearchImpl appSearchImpl = AppSearchImpl.create(appsearchDir, context, /*logger=*/ null); + AppSearchImpl appSearchImpl = + AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); List<AppSearchSchema> schemas = Collections.singletonList(new AppSearchSchema.Builder("type").build()); @@ -1838,6 +1826,7 @@ public class AppSearchImplTest { "package", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1856,7 +1845,7 @@ public class AppSearchImplTest { // That document should be visible even from another instance. AppSearchImpl appSearchImpl2 = - AppSearchImpl.create(appsearchDir, context, /*logger=*/ null); + AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); getResult = appSearchImpl2.getDocument( "package", "database", "namespace1", "id1", Collections.emptyMap()); @@ -1866,9 +1855,9 @@ public class AppSearchImplTest { @Test public void testDeletePersistsWithLiteFlush() throws Exception { // Setup the index - Context context = ApplicationProvider.getApplicationContext(); File appsearchDir = mTemporaryFolder.newFolder(); - AppSearchImpl appSearchImpl = AppSearchImpl.create(appsearchDir, context, /*logger=*/ null); + AppSearchImpl appSearchImpl = + AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); List<AppSearchSchema> schemas = Collections.singletonList(new AppSearchSchema.Builder("type").build()); @@ -1876,6 +1865,7 @@ public class AppSearchImplTest { "package", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1918,7 +1908,7 @@ public class AppSearchImplTest { // Only the second document should be retrievable from another instance. AppSearchImpl appSearchImpl2 = - AppSearchImpl.create(appsearchDir, context, /*logger=*/ null); + AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); expectThrows( AppSearchException.class, () -> @@ -1937,9 +1927,9 @@ public class AppSearchImplTest { @Test public void testDeleteByQueryPersistsWithLiteFlush() throws Exception { // Setup the index - Context context = ApplicationProvider.getApplicationContext(); File appsearchDir = mTemporaryFolder.newFolder(); - AppSearchImpl appSearchImpl = AppSearchImpl.create(appsearchDir, context, /*logger=*/ null); + AppSearchImpl appSearchImpl = + AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); List<AppSearchSchema> schemas = Collections.singletonList(new AppSearchSchema.Builder("type").build()); @@ -1947,6 +1937,7 @@ public class AppSearchImplTest { "package", "database", schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -1997,7 +1988,7 @@ public class AppSearchImplTest { // Only the second document should be retrievable from another instance. AppSearchImpl appSearchImpl2 = - AppSearchImpl.create(appsearchDir, context, /*logger=*/ null); + AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); expectThrows( AppSearchException.class, () -> diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java index f0a6ef13b212..f20e8c6d13a9 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java @@ -25,9 +25,6 @@ import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; -import android.content.Context; - -import androidx.test.core.app.ApplicationProvider; import com.android.server.appsearch.external.localstorage.stats.CallStats; import com.android.server.appsearch.external.localstorage.stats.InitializeStats; @@ -53,17 +50,16 @@ public class AppSearchLoggerTest { @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private AppSearchImpl mAppSearchImpl; private TestLogger mLogger; + /** + * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class. + */ + private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true; @Before public void setUp() throws Exception { - Context context = ApplicationProvider.getApplicationContext(); - - // Give ourselves global query permissions mAppSearchImpl = AppSearchImpl.create( - mTemporaryFolder.newFolder(), - context, - /*logger=*/ null); + mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); mLogger = new TestLogger(); } @@ -284,18 +280,15 @@ public class AppSearchLoggerTest { // @Test public void testLoggingStats_initialize() throws Exception { - Context context = ApplicationProvider.getApplicationContext(); + // Create an unused AppSearchImpl to generated an InitializeStats. + InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); + AppSearchImpl.create(mTemporaryFolder.newFolder(), initStatsBuilder, ALWAYS_OPTIMIZE); + InitializeStats iStats = initStatsBuilder.build(); - AppSearchImpl appSearchImpl = - AppSearchImpl.create( - mTemporaryFolder.newFolder(), - context, - mLogger); - - InitializeStats iStats = mLogger.mInitializeStats; assertThat(iStats).isNotNull(); assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); - assertThat(iStats.getTotalLatencyMillis()).isGreaterThan(0); + // Total latency captured in LocalStorage + assertThat(iStats.getTotalLatencyMillis()).isEqualTo(0); assertThat(iStats.hasDeSync()).isFalse(); assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0); assertThat(iStats.getDocumentStoreDataStatus()) @@ -315,6 +308,7 @@ public class AppSearchLoggerTest { testPackageName, testDatabase, schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -325,9 +319,9 @@ public class AppSearchLoggerTest { PutDocumentStats pStats = mLogger.mPutDocumentStats; assertThat(pStats).isNotNull(); - assertThat(pStats.getGeneralStats().getPackageName()).isEqualTo(testPackageName); - assertThat(pStats.getGeneralStats().getDatabase()).isEqualTo(testDatabase); - assertThat(pStats.getGeneralStats().getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); + assertThat(pStats.getPackageName()).isEqualTo(testPackageName); + assertThat(pStats.getDatabase()).isEqualTo(testDatabase); + assertThat(pStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); // The rest of native stats have been tested in testCopyNativeStats assertThat(pStats.getNativeDocumentSizeBytes()).isGreaterThan(0); } @@ -343,10 +337,12 @@ public class AppSearchLoggerTest { testPackageName, testDatabase, schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); + GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build(); mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger); @@ -394,6 +390,7 @@ public class AppSearchLoggerTest { testPackageName, testDatabase, schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -425,6 +422,7 @@ public class AppSearchLoggerTest { testPackageName, testDatabase, schemas, + /*visibilityStore=*/ null, /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java new file mode 100644 index 000000000000..f30cbb8c5158 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localstorage; + +import static com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy.BYTES_OPTIMIZE_THRESHOLD; +import static com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy.DOC_COUNT_OPTIMIZE_THRESHOLD; +import static com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy.TIME_OPTIMIZE_THRESHOLD_MILLIS; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.server.appsearch.proto.GetOptimizeInfoResultProto; +import com.android.server.appsearch.proto.StatusProto; + +import org.junit.Test; + +public class FrameworkOptimizeStrategyTest { + FrameworkOptimizeStrategy mFrameworkOptimizeStrategy = new FrameworkOptimizeStrategy(); + + @Test + public void testShouldOptimize_docCountThreshold() { + GetOptimizeInfoResultProto optimizeInfo = + GetOptimizeInfoResultProto.newBuilder() + .setTimeSinceLastOptimizeMs(0) + .setEstimatedOptimizableBytes(BYTES_OPTIMIZE_THRESHOLD) + .setOptimizableDocs(0) + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build()) + .build(); + assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue(); + } + + @Test + public void testShouldOptimize_byteThreshold() { + GetOptimizeInfoResultProto optimizeInfo = + GetOptimizeInfoResultProto.newBuilder() + .setTimeSinceLastOptimizeMs(TIME_OPTIMIZE_THRESHOLD_MILLIS) + .setEstimatedOptimizableBytes(0) + .setOptimizableDocs(0) + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build()) + .build(); + assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue(); + } + + @Test + public void testShouldNotOptimize_timeThreshold() { + GetOptimizeInfoResultProto optimizeInfo = + GetOptimizeInfoResultProto.newBuilder() + .setTimeSinceLastOptimizeMs(0) + .setEstimatedOptimizableBytes(0) + .setOptimizableDocs(DOC_COUNT_OPTIMIZE_THRESHOLD) + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build()) + .build(); + assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java index a71e53233848..6d9068675a72 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java @@ -29,57 +29,28 @@ public class AppSearchStatsTest { static final int TEST_TOTAL_LATENCY_MILLIS = 20; @Test - public void testAppSearchStats_GeneralStats() { - final GeneralStats gStats = - new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .build(); - - assertThat(gStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); - assertThat(gStats.getDatabase()).isEqualTo(TEST_DATA_BASE); - assertThat(gStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(gStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); - } - - /** Make sure status code is UNKNOWN if not set in {@link GeneralStats} */ - @Test - public void testAppSearchStats_GeneralStats_defaultStatsCode_Unknown() { - final GeneralStats gStats = - new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .build(); - - assertThat(gStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); - assertThat(gStats.getDatabase()).isEqualTo(TEST_DATA_BASE); - assertThat(gStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_UNKNOWN_ERROR); - assertThat(gStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); - } - - @Test public void testAppSearchStats_CallStats() { final int estimatedBinderLatencyMillis = 1; final int numOperationsSucceeded = 2; final int numOperationsFailed = 3; final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS; - final CallStats.Builder cStatsBuilder = - new CallStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) + final CallStats cStats = + new CallStats.Builder() + .setPackageName(TEST_PACKAGE_NAME) + .setDatabase(TEST_DATA_BASE) + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) .setCallType(callType) .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(numOperationsSucceeded) - .setNumOperationsFailed(numOperationsFailed); - cStatsBuilder - .getGeneralStatsBuilder() - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS); - final CallStats cStats = cStatsBuilder.build(); + .setNumOperationsFailed(numOperationsFailed) + .build(); - assertThat(cStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME); - assertThat(cStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE); - assertThat(cStats.getGeneralStats().getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(cStats.getGeneralStats().getTotalLatencyMillis()) - .isEqualTo(TEST_TOTAL_LATENCY_MILLIS); + assertThat(cStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); + assertThat(cStats.getDatabase()).isEqualTo(TEST_DATA_BASE); + assertThat(cStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); + assertThat(cStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); assertThat(cStats.getEstimatedBinderLatencyMillis()) .isEqualTo(estimatedBinderLatencyMillis); assertThat(cStats.getCallType()).isEqualTo(callType); @@ -88,6 +59,19 @@ public class AppSearchStatsTest { } @Test + public void testAppSearchCallStats_nullValues() { + final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS; + + final CallStats.Builder cStatsBuilder = new CallStats.Builder().setCallType(callType); + + final CallStats cStats = cStatsBuilder.build(); + + assertThat(cStats.getPackageName()).isNull(); + assertThat(cStats.getDatabase()).isNull(); + assertThat(cStats.getCallType()).isEqualTo(callType); + } + + @Test public void testAppSearchStats_PutDocumentStats() { final int generateDocumentProtoLatencyMillis = 1; final int rewriteDocumentTypesLatencyMillis = 2; @@ -100,6 +84,8 @@ public class AppSearchStatsTest { final boolean nativeExceededMaxNumTokens = true; final PutDocumentStats.Builder pStatsBuilder = new PutDocumentStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) .setGenerateDocumentProtoLatencyMillis(generateDocumentProtoLatencyMillis) .setRewriteDocumentTypesLatencyMillis(rewriteDocumentTypesLatencyMillis) .setNativeLatencyMillis(nativeLatencyMillis) @@ -109,17 +95,13 @@ public class AppSearchStatsTest { .setNativeDocumentSizeBytes(nativeDocumentSize) .setNativeNumTokensIndexed(nativeNumTokensIndexed) .setNativeExceededMaxNumTokens(nativeExceededMaxNumTokens); - pStatsBuilder - .getGeneralStatsBuilder() - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS); + final PutDocumentStats pStats = pStatsBuilder.build(); - assertThat(pStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME); - assertThat(pStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE); - assertThat(pStats.getGeneralStats().getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(pStats.getGeneralStats().getTotalLatencyMillis()) - .isEqualTo(TEST_TOTAL_LATENCY_MILLIS); + assertThat(pStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); + assertThat(pStats.getDatabase()).isEqualTo(TEST_DATA_BASE); + assertThat(pStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); + assertThat(pStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); assertThat(pStats.getGenerateDocumentProtoLatencyMillis()) .isEqualTo(generateDocumentProtoLatencyMillis); assertThat(pStats.getRewriteDocumentTypesLatencyMillis()) diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index fa3f45c08202..a2b1c1cb1a49 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; +import android.app.admin.DevicePolicyManagerLiteInternal; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; @@ -158,6 +159,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { final long ident = mContext.binder.clearCallingIdentity(); try { + LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); @@ -271,6 +273,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { final long ident = mContext.binder.clearCallingIdentity(); try { + LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); @@ -339,6 +342,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { // (Need clearCallingIdentity() to pass permission checks.) final long ident = mContext.binder.clearCallingIdentity(); try { + LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); @@ -499,6 +503,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { DevicePolicyManagerServiceTestable dpms; final long ident = mContext.binder.clearCallingIdentity(); try { + LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 5447a58a1643..cedf6361e33b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -84,6 +84,7 @@ import android.app.PendingIntent; import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; +import android.app.admin.DevicePolicyManagerLiteInternal; import android.app.admin.FactoryResetProtectionPolicy; import android.app.admin.PasswordMetrics; import android.app.admin.SystemUpdatePolicy; @@ -280,6 +281,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void initializeDpms() { // Need clearCallingIdentity() to pass permission checks. final long ident = mContext.binder.clearCallingIdentity(); + LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); @@ -369,10 +371,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(false); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); + LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); new DevicePolicyManagerServiceTestable(getServices(), mContext); // If the device has no DPMS feature, it shouldn't register the local service. assertThat(LocalServices.getService(DevicePolicyManagerInternal.class)).isNull(); + + // But should still register the lite one + assertThat(LocalServices.getService(DevicePolicyManagerLiteInternal.class)).isNotNull(); } @Test diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index f156779034ab..5ba375b922e2 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -64,6 +64,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.display.DisplayManagerService.SyncRoot; import com.android.server.lights.LightsManager; +import com.android.server.sensors.SensorManagerInternal; import com.android.server.wm.WindowManagerInternal; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; @@ -150,6 +151,7 @@ public class DisplayManagerServiceTest { @Mock LightsManager mMockLightsManager; @Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter; @Mock IBinder mMockDisplayToken; + @Mock SensorManagerInternal mMockSensorManagerInternal; @Before public void setUp() throws Exception { @@ -161,6 +163,8 @@ public class DisplayManagerServiceTest { LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal); LocalServices.removeServiceForTest(LightsManager.class); LocalServices.addService(LightsManager.class, mMockLightsManager); + LocalServices.removeServiceForTest(SensorManagerInternal.class); + LocalServices.addService(SensorManagerInternal.class, mMockSensorManagerInternal); mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 918979cdd859..cae6c863ab02 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -48,7 +48,12 @@ import android.database.ContentObserver; import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.fingerprint.IUdfpsHbmListener; import android.os.Handler; import android.os.Looper; @@ -70,6 +75,8 @@ import com.android.server.LocalServices; import com.android.server.display.DisplayModeDirector.BrightnessObserver; import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; import com.android.server.display.DisplayModeDirector.Vote; +import com.android.server.sensors.SensorManagerInternal; +import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.testutils.FakeDeviceConfigInterface; @@ -105,6 +112,10 @@ public class DisplayModeDirectorTest { public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Mock public StatusBarManagerInternal mStatusBarMock; + @Mock + public SensorManagerInternal mSensorManagerInternalMock; + @Mock + public DisplayManagerInternal mDisplayManagerInternalMock; @Before public void setUp() throws Exception { @@ -112,11 +123,15 @@ public class DisplayModeDirectorTest { mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext); when(mContext.getContentResolver()).thenReturn(resolver); - mInjector = new FakesInjector(); + mInjector = spy(new FakesInjector()); mHandler = new Handler(Looper.getMainLooper()); LocalServices.removeServiceForTest(StatusBarManagerInternal.class); LocalServices.addService(StatusBarManagerInternal.class, mStatusBarMock); + LocalServices.removeServiceForTest(SensorManagerInternal.class); + LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock); + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); } private DisplayModeDirector createDirectorFromRefreshRateArray( @@ -1214,14 +1229,193 @@ public class DisplayModeDirectorTest { assertThat(desiredSpecs.baseModeId).isEqualTo(8); } + @Test + public void testProximitySensorVoting() throws Exception { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + director.start(createMockSensorManager()); + + ArgumentCaptor<ProximityActiveListener> captor = + ArgumentCaptor.forClass(ProximityActiveListener.class); + verify(mSensorManagerInternalMock).addProximityActiveListener(any(Executor.class), + captor.capture()); + ProximityActiveListener listener = captor.getValue(); + + // Verify that there is no proximity vote initially + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertNull(vote); + + when(mDisplayManagerInternalMock.getRefreshRateForDisplayAndSensor(eq(DISPLAY_ID), eq(null), + eq(Sensor.STRING_TYPE_PROXIMITY))).thenReturn(new RefreshRateRange(60, 60)); + + // Set the proximity to active and verify that we added a vote. + listener.onProximityActive(true); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertVoteForRefreshRate(vote, 60.f); + + // Turn prox off and verify vote is gone. + listener.onProximityActive(false); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertNull(vote); + } + + @Test + public void testHbmVoting_forHdr() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0); + director.start(createMockSensorManager()); + + ArgumentCaptor<DisplayListener> captor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS + | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED)); + DisplayListener listener = captor.getValue(); + + // Specify Limitation + when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn( + List.of(new RefreshRateLimitation( + DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, + 60.f, 60.f))); + + // Verify that there is no HBM vote initially + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn on HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertVoteForRefreshRate(vote, 60.f); + + // Turn off HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + } + + @Test + public void testHbmVoting_forSunlight() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0); + director.start(createMockSensorManager()); + + ArgumentCaptor<DisplayListener> captor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS + | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED)); + DisplayListener listener = captor.getValue(); + + // Specify Limitation + when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn( + List.of(new RefreshRateLimitation( + DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, + 60.f, 60.f))); + + // Verify that there is no HBM vote initially + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn on HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertVoteForRefreshRate(vote, 60.f); + + // Turn off HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + } + + @Test + public void testHbmVoting_forSunlight_NoLimitation() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0); + director.start(createMockSensorManager()); + + ArgumentCaptor<DisplayListener> captor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS + | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED)); + DisplayListener listener = captor.getValue(); + + // Specify Limitation for different display + when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID + 1)).thenReturn( + List.of(new RefreshRateLimitation( + DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, + 60.f, 60.f))); + + // Verify that there is no HBM vote initially + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn on HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn off HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + } + + @Test + public void testHbmVoting_RemovedDisplay() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0); + director.start(createMockSensorManager()); + + ArgumentCaptor<DisplayListener> captor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS + | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED)); + DisplayListener listener = captor.getValue(); + + // Specify Limitation for different display + when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn( + List.of(new RefreshRateLimitation( + DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, + 60.f, 60.f))); + + // Verify that there is no HBM vote initially + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn on HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertVoteForRefreshRate(vote, 60.f); + + // Turn off HBM + listener.onDisplayRemoved(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + } + private void assertVoteForRefreshRate(Vote vote, float refreshRate) { assertThat(vote).isNotNull(); - final DisplayModeDirector.RefreshRateRange expectedRange = - new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate); + final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate); assertThat(vote.refreshRateRange).isEqualTo(expectedRange); } - private static class FakeDeviceConfig extends FakeDeviceConfigInterface { + public static class FakeDeviceConfig extends FakeDeviceConfigInterface { @Override public String getProperty(String namespace, String name) { Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace)); @@ -1362,7 +1556,7 @@ public class DisplayModeDirectorTest { mHandler.runWithScissors(() -> { }, 500 /*timeout*/); } - static class FakesInjector implements DisplayModeDirector.Injector { + public static class FakesInjector implements DisplayModeDirector.Injector { private final FakeDeviceConfig mDeviceConfig; private ContentObserver mBrightnessObserver; private ContentObserver mPeakRefreshRateObserver; @@ -1403,6 +1597,14 @@ public class DisplayModeDirectorTest { mPeakRefreshRateObserver = observer; } + @Override + public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {} + + @Override + public BrightnessInfo getBrightnessInfo(int displayId) { + return null; + } + void notifyPeakRefreshRateChanged() { if (mPeakRefreshRateObserver != null) { mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/, diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 0cf212c20c1e..f53ae525eaba 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -40,6 +40,7 @@ import android.content.ContextWrapper; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener; +import android.hardware.hdmi.IHdmiControlStatusChangeListener; import android.os.Binder; import android.os.IPowerManager; import android.os.IThermalService; @@ -683,6 +684,100 @@ public class HdmiControlServiceTest { HdmiControlManager.HDMI_CEC_VERSION_2_0); } + @Test + public void initCec_statusListener_CecDisabled() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isFalse(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isFalse(); + } + + @Test + public void initCec_statusListener_CecEnabled_NoCecResponse() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mTestLooper.dispatchAll(); + // Hit timeout twice due to retries + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isFalse(); + } + + @Test + public void initCec_statusListener_CecEnabled_CecAvailable_TvOn() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_TV, + mHdmiControlServiceSpy.playback().mAddress, HdmiControlManager.POWER_STATUS_ON); + mNativeWrapper.onCecMessage(reportPowerStatus); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue(); + } + + @Test + public void initCec_statusListener_CecEnabled_CecAvailable_TvStandby() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_TV, + mHdmiControlServiceSpy.playback().mAddress, + HdmiControlManager.POWER_STATUS_STANDBY); + mNativeWrapper.onCecMessage(reportPowerStatus); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue(); + } + + private static class HdmiControlStatusCallback extends IHdmiControlStatusChangeListener.Stub { + boolean mCecEnabled = false; + boolean mCecAvailable = false; + + @Override + public void onStatusChange(int isCecEnabled, boolean isCecAvailable) + throws RemoteException { + mCecEnabled = isCecEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; + mCecAvailable = isCecAvailable; + } + } + private static class VolumeControlFeatureCallback extends IHdmiCecVolumeControlFeatureListener.Stub { boolean mCallbackReceived = false; diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index 2efebbf044c9..8eb3cf3ca418 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -4,7 +4,9 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; @@ -102,6 +104,20 @@ public class JobStoreTest { } @Test + public void testStringToIntArrayAndIntArrayToString() { + final int[] netCapabilitiesIntArray = { 1, 3, 5, 7, 9 }; + final String netCapabilitiesStr = "1,3,5,7,9"; + final String netCapabilitiesStrWithErrorInt = "1,3,a,7,9"; + final String emptyString = ""; + final String str1 = JobStore.intArrayToString(netCapabilitiesIntArray); + assertArrayEquals(netCapabilitiesIntArray, JobStore.stringToIntArray(str1)); + assertEquals(0, JobStore.stringToIntArray(emptyString).length); + assertThrows(NumberFormatException.class, + () -> JobStore.stringToIntArray(netCapabilitiesStrWithErrorInt)); + assertEquals(netCapabilitiesStr, JobStore.intArrayToString(netCapabilitiesIntArray)); + } + + @Test public void testMaybeWriteStatusToDisk() throws Exception { int taskId = 5; long runByMillis = 20000L; // 20s diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index 67dd0556e098..dc745cdc0c84 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -83,9 +83,17 @@ public class AppsFilterTest { private static final int DUMMY_OVERLAY_APPID = 10756; private static final int SYSTEM_USER = 0; private static final int SECONDARY_USER = 10; + private static final int ADDED_USER = 11; private static final int[] USER_ARRAY = {SYSTEM_USER, SECONDARY_USER}; - private static final UserInfo[] USER_INFO_LIST = Arrays.stream(USER_ARRAY).mapToObj( - id -> new UserInfo(id, Integer.toString(id), 0)).toArray(UserInfo[]::new); + private static final int[] USER_ARRAY_WITH_ADDED = {SYSTEM_USER, SECONDARY_USER, ADDED_USER}; + private static final UserInfo[] USER_INFO_LIST = toUserInfos(USER_ARRAY); + private static final UserInfo[] USER_INFO_LIST_WITH_ADDED = toUserInfos(USER_ARRAY_WITH_ADDED); + + private static UserInfo[] toUserInfos(int[] userIds) { + return Arrays.stream(userIds) + .mapToObj(id -> new UserInfo(id, Integer.toString(id), 0)) + .toArray(UserInfo[]::new); + } @Mock AppsFilter.FeatureConfig mFeatureConfigMock; @@ -319,6 +327,47 @@ public class AppsFilterTest { } @Test + public void testOnUserCreated_FilterMatches() throws Exception { + final AppsFilter appsFilter = + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); + simulateAddBasicAndroid(appsFilter); + + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, + pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID); + PackageSetting calling = simulateAddPackage(appsFilter, + pkgQueriesProvider("com.some.other.package", "com.some.authority"), + DUMMY_CALLING_APPID); + + for (int subjectUserId : USER_ARRAY) { + for (int otherUserId : USER_ARRAY) { + assertFalse(appsFilter.shouldFilterApplication( + UserHandle.getUid(DUMMY_CALLING_APPID, subjectUserId), calling, target, + otherUserId)); + } + } + + // adds new user + doAnswer(invocation -> { + ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) + .currentState(mExisting, USER_INFO_LIST_WITH_ADDED); + return new Object(); + }).when(mStateProvider) + .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + appsFilter.onUserCreated(ADDED_USER); + + for (int subjectUserId : USER_ARRAY_WITH_ADDED) { + for (int otherUserId : USER_ARRAY_WITH_ADDED) { + assertFalse(appsFilter.shouldFilterApplication( + UserHandle.getUid(DUMMY_CALLING_APPID, subjectUserId), calling, target, + otherUserId)); + } + } + } + + @Test public void testQueriesDifferentProvider_Filters() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index 558fb309ad98..976a588273a7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -18,7 +18,11 @@ package com.android.server.pm; import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.fail; + import static java.lang.reflect.Modifier.isFinal; +import static java.lang.reflect.Modifier.isPrivate; +import static java.lang.reflect.Modifier.isProtected; import static java.lang.reflect.Modifier.isPublic; import static java.lang.reflect.Modifier.isStatic; @@ -44,9 +48,12 @@ import org.junit.runner.RunWith; import java.io.File; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.regex.Pattern; @@ -393,6 +400,178 @@ public class PackageManagerServiceTest { Assert.assertEquals(3600000003L, multiPackage[1].timeouts.maxPendingTimeUs); } + // Report an error from the Computer structure validation test. + private void flag(String name, String msg) { + fail(name + " " + msg); + } + + // Return a string that identifies a Method. This is not very efficient but it is not + // called very often. + private String displayName(Method m) { + String r = m.getName(); + String p = Arrays.toString(m.getGenericParameterTypes()) + .replaceAll("([a-zA-Z0-9]+\\.)+", "") + .replace("class ", "") + .replaceAll("^\\[", "(") + .replaceAll("\\]$", ")"); + return r + p; + } + + // Match a method to an array of Methods. Matching is on method signature: name and + // parameter types. If a method in the declared array matches, return it. Otherwise + // return null. + private Method matchMethod(Method m, Method[] declared) { + String n = m.getName(); + Type[] t = m.getGenericParameterTypes(); + for (int i = 0; i < declared.length; i++) { + Method l = declared[i]; + if (l != null && l.getName().equals(n) + && Arrays.equals(l.getGenericParameterTypes(), t)) { + Method result = l; + // Set the method to null since it has been visited already. + declared[i] = null; + return result; + } + } + return null; + } + + // Return the boolean locked value. A null return means the annotation was not + // found. This method will fail if the annotation is found but is not one of the + // known constants. + private Boolean getOverride(Method m) { + final String name = "Computer." + displayName(m); + final PackageManagerService.Computer.LiveImplementation annotation = + m.getAnnotation(PackageManagerService.Computer.LiveImplementation.class); + if (annotation == null) { + return null; + } + final int override = annotation.override(); + if (override == PackageManagerService.Computer.LiveImplementation.MANDATORY) { + return true; + } else if (override == PackageManagerService.Computer.LiveImplementation.NOT_ALLOWED) { + return false; + } else { + flag(name, "invalid Live value: " + override); + return null; + } + } + + @Test + public void testComputerStructure() { + // Verify that Copmuter methods are properly annotated and that ComputerLocked is + // properly populated per annotations. + // Call PackageManagerService.validateComputer(); + Class base = PackageManagerService.Computer.class; + + HashMap<Method, Boolean> methodType = new HashMap<>(); + + // Verify that all Computer methods are annotated and that the annotation + // parameter locked() is valid. + for (Method m : base.getDeclaredMethods()) { + final String name = "Computer." + displayName(m); + Boolean override = getOverride(m); + if (override == null) { + flag(name, "missing required Live annotation"); + } + methodType.put(m, override); + } + + Class coreClass = PackageManagerService.ComputerEngine.class; + final Method[] coreMethods = coreClass.getDeclaredMethods(); + + // Examine every method in the core. If it inherits from a base method it must be + // "public final" if the base is NOT_ALLOWED or "public" if the base is MANDATORY. + // If the core method does not inherit from the base then it must be either + // private or protected. + for (Method m : base.getDeclaredMethods()) { + String name = "Computer." + displayName(m); + final boolean locked = methodType.get(m); + final Method core = matchMethod(m, coreMethods); + if (core == null) { + flag(name, "not overridden in ComputerEngine"); + continue; + } + name = "ComputerEngine." + displayName(m); + final int modifiers = core.getModifiers(); + if (!locked) { + if (!isPublic(modifiers)) { + flag(name, "is not public"); + } + if (!isFinal(modifiers)) { + flag(name, "is not final"); + } + } + } + // Any methods left in the coreMethods array must be private or protected. + // Protected methods must be overridden (and final) in the live list. + Method[] coreHelpers = new Method[coreMethods.length]; + int coreIndex = 0; + for (Method m : coreMethods) { + if (m != null) { + final String name = "ComputerEngine." + displayName(m); + final int modifiers = m.getModifiers(); + if (isPrivate(modifiers)) { + // Okay + } else if (isProtected(modifiers)) { + coreHelpers[coreIndex++] = m; + } else { + flag(name, "is neither private nor protected"); + } + } + } + + Class liveClass = PackageManagerService.ComputerLocked.class; + final Method[] liveMethods = liveClass.getDeclaredMethods(); + + // Examine every method in the live list. Every method must be final and must + // inherit either from base or core. If the method inherits from a base method + // then the base must be MANDATORY. + for (Method m : base.getDeclaredMethods()) { + String name = "Computer." + displayName(m); + final boolean locked = methodType.get(m); + final Method live = matchMethod(m, liveMethods); + if (live == null) { + if (locked) { + flag(name, "not overridden in ComputerLocked"); + } + continue; + } + if (!locked) { + flag(name, "improperly overridden in ComputerLocked"); + continue; + } + + name = "ComputerLocked." + displayName(m); + final int modifiers = live.getModifiers(); + if (!locked) { + if (!isPublic(modifiers)) { + flag(name, "is not public"); + } + if (!isFinal(modifiers)) { + flag(name, "is not final"); + } + } + } + for (Method m : coreHelpers) { + if (m == null) { + continue; + } + String name = "ComputerLocked." + displayName(m); + final Method live = matchMethod(m, liveMethods); + if (live == null) { + flag(name, "is not overridden in ComputerLocked"); + continue; + } + } + for (Method m : liveMethods) { + if (m != null) { + String name = "ComputerLocked." + displayName(m); + flag(name, "illegal local method"); + } + } + } + private static PerPackageReadTimeouts[] getPerPackageReadTimeouts(String knownDigestersList) { final String defaultTimeouts = "3600000001:3600000002:3600000003"; List<PerPackageReadTimeouts> result = PerPackageReadTimeouts.parseDigestersList( diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 7241fa00ecf7..90a127701505 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -253,6 +253,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setPerson(makePerson("person", "personKey", "personUri")) .setLongLived(true) .setExtras(pb) + .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); @@ -288,6 +289,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(null, si.getTextResName()); assertEquals(0, si.getDisabledMessageResourceId()); assertEquals(null, si.getDisabledMessageResName()); + assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen", + si.getStartingThemeResName()); } public void testShortcutInfoParcel_resId() { @@ -308,6 +311,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setRank(123) .setExtras(pb) + .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); @@ -339,6 +343,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); assertEquals("test_uri", si.getIconUri()); + assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen", + si.getStartingThemeResName()); } public void testShortcutInfoClone() { @@ -2210,6 +2216,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { android.R.drawable.alert_dark_frame, true, getTestContext().getPackageName())); assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res, android.R.string.cancel, false, getTestContext().getPackageName())); + assertEquals("" + android.R.style.Theme_Black_NoTitleBar_Fullscreen, + ShortcutInfo.lookUpResourceName( + res, android.R.style.Theme_Black_NoTitleBar_Fullscreen, true, + getTestContext().getPackageName())); } public void testLookUpResourceName_appResources() { @@ -2236,6 +2246,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceId(res, "" + android.R.drawable.alert_dark_frame, null, getTestContext().getPackageName())); + assertEquals(android.R.style.Theme_Black_NoTitleBar_Fullscreen, + ShortcutInfo.lookUpResourceId( + res, "" + android.R.style.Theme_Black_NoTitleBar_Fullscreen, + null, getTestContext().getPackageName())); } // Test for a ShortcutInfo method. diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index 17d99e652726..f880563e2880 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -222,6 +222,64 @@ public class SystemConfigTest { } /** + * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_APP_CONFIGS} + * permission flag for the tag: {@code allowed-partner-apex}. + */ + @Test + public void readPermissions_allowAppConfigs_parsesPartnerApexAllowList() + throws IOException { + final String contents = + "<config>\n" + + " <allowed-partner-apex package=\"com.android.apex1\" />\n" + + "</config>"; + final File folder = createTempSubfolder("folder"); + createTempFile(folder, "partner-apex-allowlist.xml", contents); + + mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0); + + assertThat(mSysConfig.getAllowedPartnerApexes()).containsExactly("com.android.apex1"); + } + + /** + * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_APP_CONFIGS} + * permission flag for the tag: {@code allowed-partner-apex}. + */ + @Test + public void readPermissions_allowAppConfigs_parsesPartnerApexAllowList_noPackage() + throws IOException { + final String contents = + "<config>\n" + + " <allowed-partner-apex/>\n" + + "</config>"; + final File folder = createTempSubfolder("folder"); + createTempFile(folder, "partner-apex-allowlist.xml", contents); + + mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0); + + assertThat(mSysConfig.getAllowedPartnerApexes()).isEmpty(); + } + + + /** + * Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_APP_CONFIGS} + * permission flag for the tag: {@code allowed-partner-apex}. + */ + @Test + public void readPermissions_notAllowAppConfigs_doesNotParsePartnerApexAllowList() + throws IOException { + final String contents = + "<config>\n" + + " <allowed-partner-apex package=\"com.android.apex1\" />\n" + + "</config>"; + final File folder = createTempSubfolder("folder"); + createTempFile(folder, "partner-apex-allowlist.xml", contents); + + mSysConfig.readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08); + + assertThat(mSysConfig.getAllowedPartnerApexes()).isEmpty(); + } + + /** * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents. * * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index a24691791938..4c157c0e0a73 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -1179,6 +1179,36 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); } + /** + * Test that an app that "timed out" into the RESTRICTED bucket can be raised out by system + * interaction. + */ + @Test + public void testSystemInteractionOverridesRestrictedTimeout() throws Exception { + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Long enough that it could have timed out into RESTRICTED. + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_RESTRICTED); + + // Report system interaction. + mInjector.mElapsedRealtime += 1000; + reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + + // Ensure that it's raised out of RESTRICTED for the system interaction elevation duration. + assertBucket(STANDBY_BUCKET_ACTIVE); + mInjector.mElapsedRealtime += 1000; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Elevation duration over. Should fall back down. + mInjector.mElapsedRealtime += 10 * MINUTE_MS; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_RESTRICTED); + } + @Test public void testRestrictedBucketDisabled() throws Exception { mInjector.mIsRestrictedBucketEnabled = false; diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java index 855012459bd6..7d24a2f5845e 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -33,6 +33,7 @@ import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.media.AudioManager; import android.os.Handler; import android.os.PowerManagerInternal; @@ -69,6 +70,7 @@ import org.mockito.junit.MockitoRule; public class VibrationSettingsTest { private static final int UID = 1; + private static final int USER_OPERATION_TIMEOUT_MILLIS = 60_000; // 1 min private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build(); private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder() .setBatterySaverEnabled(true).build(); @@ -408,6 +410,25 @@ public class VibrationSettingsTest { } @Test + public void getCurrentIntensity_updateTriggeredAfterUserSwitched() { + mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_HIGH); + assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH, + mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE)); + + // Switching user is not working with FakeSettingsProvider. + // Testing the broadcast flow manually. + Settings.System.putIntForUser(mContextSpy.getContentResolver(), + Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW, + UserHandle.USER_CURRENT); + mVibrationSettings.mUserReceiver.onReceive(mContextSpy, + new Intent(Intent.ACTION_USER_SWITCHED)); + assertEquals(Vibrator.VIBRATION_INTENSITY_LOW, + mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE)); + } + + @Test public void getFallbackEffect_returnsEffectsFromSettings() { assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TICK)); assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TEXTURE_TICK)); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index ea2dc0a8a345..9117ae696e9f 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -234,7 +234,7 @@ public class VibratorManagerServiceTest { CombinedVibration effect = CombinedVibration.createParallel( VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS); - service.cancelVibrate(/* usageFilter= */ -1, service); + service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service); assertTrue(service.setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS)); @@ -889,13 +889,13 @@ public class VibratorManagerServiceTest { mockVibrators(1); VibratorManagerService service = createSystemReadyService(); - service.cancelVibrate(/* usageFilter= */ -1, service); + service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service); assertFalse(service.isVibrating(1)); vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), ALARM_ATTRS); assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); - service.cancelVibrate(/* usageFilter= */ -1, service); + service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service); assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); } @@ -924,6 +924,39 @@ public class VibratorManagerServiceTest { assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); } + @Test + public void cancelVibrate_withoutUnknownUsage_onlyStopsIfFilteringUnknownOrAllUsages() + throws Exception { + mockVibrators(1); + VibrationAttributes attrs = new VibrationAttributes.Builder() + .setUsage(VibrationAttributes.USAGE_UNKNOWN) + .build(); + VibratorManagerService service = createSystemReadyService(); + + vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), attrs); + assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + + // Do not cancel UNKNOWN vibration when filter is being applied for other usages. + service.cancelVibrate(VibrationAttributes.USAGE_RINGTONE, service); + assertFalse(waitUntil(s -> !s.isVibrating(1), service, /* timeout= */ 50)); + + service.cancelVibrate( + VibrationAttributes.USAGE_CLASS_ALARM | ~VibrationAttributes.USAGE_CLASS_MASK, + service); + assertFalse(waitUntil(s -> !s.isVibrating(1), service, /* timeout= */ 50)); + + // Cancel UNKNOWN vibration when filtered for that vibration specifically. + service.cancelVibrate(VibrationAttributes.USAGE_UNKNOWN, service); + assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + + vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), attrs); + assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + + // Cancel UNKNOWN vibration when all vibrations are being cancelled. + service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service); + assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + } + private void mockCapabilities(long... capabilities) { when(mNativeWrapperMock.getCapabilities()).thenReturn( Arrays.stream(capabilities).reduce(0, (a, b) -> a | b)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index 5614aa2a165d..577e36c7d5db 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -394,7 +394,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { "disabledMessage", 0, "disabledMessageResName", null, null, 0, null, 0, 0, 0, "iconResName", "bitmapPath", null, 0, - null, null, 0); + null, null, null); return si; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 70ba2cf17880..a522b5c6161e 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -6472,7 +6472,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { zenPolicy, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled); try { - mBinderService.addAutomaticZenRule(rule); + mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); fail("Zen policy only applies to priority only mode"); } catch (IllegalArgumentException e) { // yay @@ -6480,11 +6480,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); - mBinderService.addAutomaticZenRule(rule); + mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), null, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled); - mBinderService.addAutomaticZenRule(rule); + mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 5262465a399c..d0bf63a1680f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -190,7 +190,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); rule.configurationActivity = new ComponentName("a", "a"); - rule.component = new ComponentName("a", "b"); + rule.component = new ComponentName("b", "b"); rule.conditionId = new Uri.Builder().scheme("hello").build(); rule.condition = new Condition(rule.conditionId, "", Condition.STATE_TRUE); rule.enabled = true; @@ -200,6 +200,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.modified = true; rule.name = "name"; rule.snoozing = true; + rule.pkg = "b"; TypedXmlSerializer out = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -215,8 +216,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser); - // read from backing component - assertEquals("a", fromXml.pkg); + assertEquals("b", fromXml.pkg); // always resets on reboot assertFalse(fromXml.snoozing); //should all match original @@ -232,6 +232,55 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(rule.zenMode, fromXml.zenMode); } + @Test + public void testRuleXml_pkg_component() throws Exception { + String tag = "tag"; + + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + rule.configurationActivity = new ComponentName("a", "a"); + rule.component = new ComponentName("b", "b"); + + TypedXmlSerializer out = Xml.newFastSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + out.setOutput(new BufferedOutputStream(baos), "utf-8"); + out.startDocument(null, true); + out.startTag(null, tag); + ZenModeConfig.writeRuleXml(rule, out); + out.endTag(null, tag); + out.endDocument(); + + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), null); + parser.nextTag(); + ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser); + assertEquals("b", fromXml.pkg); + } + + @Test + public void testRuleXml_pkg_configActivity() throws Exception { + String tag = "tag"; + + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + rule.configurationActivity = new ComponentName("a", "a"); + + TypedXmlSerializer out = Xml.newFastSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + out.setOutput(new BufferedOutputStream(baos), "utf-8"); + out.startDocument(null, true); + out.startTag(null, tag); + ZenModeConfig.writeRuleXml(rule, out); + out.endTag(null, tag); + out.endDocument(); + + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), null); + parser.nextTag(); + ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser); + assertNull(fromXml.pkg); + } + private ZenModeConfig getMutedRingerConfig() { ZenModeConfig config = new ZenModeConfig(); // Allow alarms, media diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 72c6028e5ad3..00dbaf649ca2 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -1597,7 +1597,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ComponentName("android", "ScheduleConditionProvider"), ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test"); assertTrue(id != null); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id); @@ -1617,12 +1617,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ComponentName("android", "ScheduleConditionProvider"), sharedUri, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test"); AutomaticZenRule zenRule2 = new AutomaticZenRule("name2", new ComponentName("android", "ScheduleConditionProvider"), sharedUri, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id2 = mZenModeHelperSpy.addAutomaticZenRule(zenRule2, "test"); + String id2 = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule2, "test"); Condition condition = new Condition(sharedUri, "", Condition.STATE_TRUE); mZenModeHelperSpy.setAutomaticZenRuleState(sharedUri, condition); 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 821683043804..b7713a9338de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -2699,6 +2699,85 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse("Starting window should not be present", activity.hasStartingWindow()); } + @Test + public void testSetVisibility_visibleToVisible() { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true).build(); + // By default, activity is visible. + assertTrue(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + + // Request the activity to be visible. Although the activity is already visible, app + // transition animation should be applied on this activity. This might be unnecessary, but + // until we verify no logic relies on this behavior, we'll keep this as is. + activity.setVisibility(true); + assertTrue(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + } + + @Test + public void testSetVisibility_visibleToInvisible() { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true).build(); + // By default, activity is visible. + assertTrue(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + + // Request the activity to be invisible. Since the visibility changes, app transition + // animation should be applied on this activity. + activity.setVisibility(false); + assertTrue(activity.isVisible()); + assertFalse(activity.mVisibleRequested); + assertFalse(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertTrue(activity.mDisplayContent.mClosingApps.contains(activity)); + } + + @Test + public void testSetVisibility_invisibleToVisible() { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true).setVisible(false).build(); + // Activiby is invisible. However ATMS requests it to become visible, since this is a top + // activity. + assertFalse(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + + // Request the activity to be visible. Since the visibility changes, app transition + // animation should be applied on this activity. + activity.setVisibility(true); + assertFalse(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + } + + @Test + public void testSetVisibility_invisibleToInvisible() { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true).setVisible(false).build(); + // Activiby is invisible. However ATMS requests it to become visible, since this is a top + // activity. + assertFalse(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + + // Request the activity to be invisible. Since the activity is already invisible, no app + // transition should be applied on this activity. + activity.setVisibility(false); + assertFalse(activity.isVisible()); + assertFalse(activity.mVisibleRequested); + assertFalse(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + } + private void assertHasStartingWindow(ActivityRecord atoken) { assertNotNull(atoken.mStartingSurface); assertNotNull(atoken.mStartingData); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 9267285b446b..9cf29d4dcc50 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -53,6 +54,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.refEq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -1012,12 +1014,26 @@ public class RootWindowContainerTests extends WindowTestsBase { // Create another activity on top and the user id is 1 final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task) .setUid(UserHandle.PER_USER_RANGE + 1).build(); + doReturn(true).when(topActivity).okToShowLocked(); + topActivity.intent.setAction(Intent.ACTION_MAIN); // Make sure the listeners will be notified for putting the task to locked state TaskChangeNotificationController controller = mAtm.getTaskChangeNotificationController(); spyOn(controller); mWm.mRoot.lockAllProfileTasks(0); verify(controller).notifyTaskProfileLocked(eq(taskId), eq(0)); + + // Create the work lock activity on top of the task + final ActivityRecord workLockActivity = new ActivityBuilder(mAtm).setTask(task) + .setUid(UserHandle.PER_USER_RANGE + 1).build(); + doReturn(true).when(workLockActivity).okToShowLocked(); + workLockActivity.intent.setAction(ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER); + doReturn(workLockActivity.mActivityComponent).when(mAtm).getSysUiServiceComponentLocked(); + + // Make sure the listener won't be notified again. + clearInvocations(controller); + mWm.mRoot.lockAllProfileTasks(0); + verify(controller, never()).notifyTaskProfileLocked(anyInt(), anyInt()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index d2270b55b954..4e261deb67f6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -1534,30 +1534,6 @@ public class SizeCompatTests extends WindowTestsBase { } @Test - public void testSandboxDisplayApis_unresizableAppNotSandboxed() { - // Set up a display in landscape with an unresizable app. - setUpDisplaySizeWithApp(2500, 1000); - mActivity.mDisplayContent.setSandboxDisplayApis(false /* sandboxDisplayApis */); - prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE); - assertFitted(); - - // Activity max bounds not be sandboxed since sandboxing is disabled. - assertMaxBoundsInheritDisplayAreaBounds(); - } - - @Test - public void testSandboxDisplayApis_unresizableAppSandboxed() { - // Set up a display in landscape with an unresizable app. - setUpDisplaySizeWithApp(2500, 1000); - mActivity.mDisplayContent.setSandboxDisplayApis(true /* sandboxDisplayApis */); - prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE); - assertFitted(); - - // Activity max bounds should be sandboxed since sandboxing is enabled. - assertActivityMaxBoundsSandboxed(); - } - - @Test public void testResizableApp_notSandboxed() { // Set up a display in landscape with a fully resizable app. setUpDisplaySizeWithApp(2500, 1000); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index e9e201327aa4..97afc165624a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -214,8 +214,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { spyOn(mDisplayContent); spyOn(mDisplayContent.mInputMethodWindow); when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(true); - // Intentionally set the IME window is in drawn state. - doReturn(true).when(mDisplayContent.mInputMethodWindow).isDrawn(); + // Intentionally set the IME window is in visible state. + doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); // Verify no NPE happens when calling createTaskSnapshot. try { final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 3f1248a5fff7..a1b3159825fb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -796,6 +796,9 @@ public class WindowOrganizerTests extends WindowTestsBase { @Override public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { } + @Override + public void onAppSplashScreenViewRemoved(int taskId) { + } }; private ActivityRecord makePipableActivity() { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 111449d2c100..588089996d6c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -805,6 +805,7 @@ class WindowTestsBase extends SystemServiceTestsBase { private Bundle mIntentExtras; private boolean mOnTop = false; private ActivityInfo.WindowLayout mWindowLayout; + private boolean mVisible = true; ActivityBuilder(ActivityTaskManagerService service) { mService = service; @@ -930,6 +931,11 @@ class WindowTestsBase extends SystemServiceTestsBase { return this; } + ActivityBuilder setVisible(boolean visible) { + mVisible = visible; + return this; + } + ActivityRecord build() { SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock); try { @@ -1012,9 +1018,10 @@ class WindowTestsBase extends SystemServiceTestsBase { // root tasks (e.g. home root task). mTask.moveToFront("createActivity"); } - // Make visible by default... - activity.mVisibleRequested = true; - activity.setVisible(true); + if (mVisible) { + activity.mVisibleRequested = true; + activity.setVisible(true); + } } final WindowProcessController wpc; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 17303a4aa7e1..beaca68b9a37 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -29,11 +29,7 @@ import android.content.Context; import android.content.Intent; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; -import android.media.AudioAttributes; import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioRecord; -import android.media.MediaRecorder; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -66,12 +62,16 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; +import java.time.Duration; +import java.time.Instant; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; /** * A class that provides the communication with the HotwordDetectionService. @@ -81,33 +81,38 @@ final class HotwordDetectionConnection { // TODO (b/177502877): Set the Debug flag to false before shipping. private static final boolean DEBUG = true; - // Number of bytes per sample of audio (which is a short). - private static final int BYTES_PER_SAMPLE = 2; // TODO: These constants need to be refined. private static final long VALIDATION_TIMEOUT_MILLIS = 3000; - private static final long VOICE_INTERACTION_TIMEOUT_TO_OPEN_MIC_MILLIS = 2000; - private static final int MAX_STREAMING_SECONDS = 10; - private static final int MICROPHONE_BUFFER_LENGTH_SECONDS = 8; - private static final int HOTWORD_AUDIO_LENGTH_SECONDS = 3; private static final long MAX_UPDATE_TIMEOUT_MILLIS = 6000; + private static final Duration MAX_UPDATE_TIMEOUT_DURATION = + Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS); private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool(); // TODO: This may need to be a Handler(looper) private final ScheduledExecutorService mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); - private final AtomicBoolean mUpdateStateFinish = new AtomicBoolean(false); + private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false); + private final @NonNull ServiceConnectionFactory mServiceConnectionFactory; final Object mLock; final int mVoiceInteractionServiceUid; final ComponentName mDetectionComponentName; final int mUser; final Context mContext; - final @NonNull ServiceConnector<IHotwordDetectionService> mRemoteHotwordDetectionService; - boolean mBound; volatile HotwordDetectionServiceIdentity mIdentity; + private IHotwordRecognitionStatusCallback mCallback; + private IMicrophoneHotwordDetectionVoiceInteractionCallback mSoftwareCallback; + private Instant mLastRestartInstant; + + private ScheduledFuture<?> mCancellationTaskFuture; @GuardedBy("mLock") private ParcelFileDescriptor mCurrentAudioSink; + @GuardedBy("mLock") + private boolean mValidatingDspTrigger = false; + @GuardedBy("mLock") + private boolean mPerformingSoftwareHotwordDetection; + private @NonNull ServiceConnection mRemoteHotwordDetectionService; HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid, ComponentName serviceName, int userId, boolean bindInstantServiceAllowed, @@ -121,50 +126,36 @@ final class HotwordDetectionConnection { final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE); intent.setComponent(mDetectionComponentName); - mRemoteHotwordDetectionService = new ServiceConnector.Impl<IHotwordDetectionService>( - mContext, intent, bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, mUser, - IHotwordDetectionService.Stub::asInterface) { - @Override // from ServiceConnector.Impl - protected void onServiceConnectionStatusChanged(IHotwordDetectionService service, - boolean connected) { - if (DEBUG) { - Slog.d(TAG, "onServiceConnectionStatusChanged connected = " + connected); - } - synchronized (mLock) { - mBound = connected; - } - } + mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed); - @Override - protected long getAutoDisconnectTimeoutMs() { - return -1; - } + mRemoteHotwordDetectionService = mServiceConnectionFactory.create(); - @Override - public void binderDied() { - super.binderDied(); - Slog.w(TAG, "binderDied"); - try { - callback.onError(-1); - } catch (RemoteException e) { - Slog.w(TAG, "Failed to report onError status: " + e); - } - } - }; - mRemoteHotwordDetectionService.connect(); if (callback == null) { updateStateLocked(options, sharedMemory); return; } - updateAudioFlinger(); - updateContentCaptureManager(); - updateStateWithCallbackLocked(options, sharedMemory, callback); + mCallback = callback; + + mLastRestartInstant = Instant.now(); + updateStateAfterProcessStart(options, sharedMemory); + + // TODO(volnov): we need to be smarter here, e.g. schedule it a bit more often, but wait + // until the current session is closed. + mCancellationTaskFuture = mScheduledExecutorService.scheduleAtFixedRate(() -> { + if (DEBUG) { + Slog.i(TAG, "Time to restart the process, TTL has passed"); + } + + synchronized (mLock) { + restartProcessLocked(); + } + }, 30, 30, TimeUnit.MINUTES); } - private void updateStateWithCallbackLocked(PersistableBundle options, - SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) { + private void updateStateAfterProcessStart( + PersistableBundle options, SharedMemory sharedMemory) { if (DEBUG) { - Slog.d(TAG, "updateStateWithCallbackLocked"); + Slog.d(TAG, "updateStateAfterProcessStart"); } mRemoteHotwordDetectionService.postAsync(service -> { AndroidFuture<Void> future = new AndroidFuture<>(); @@ -183,21 +174,21 @@ final class HotwordDetectionConnection { mIdentity = new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid); future.complete(null); + if (mUpdateStateAfterStartFinished.getAndSet(true)) { + Slog.w(TAG, "call callback after timeout"); + return; + } + int status = bundle != null ? bundle.getInt( + KEY_INITIALIZATION_STATUS, + INITIALIZATION_STATUS_UNKNOWN) + : INITIALIZATION_STATUS_UNKNOWN; + // Add the protection to avoid unexpected status + if (status > HotwordDetectionService.getMaxCustomInitializationStatus() + && status != INITIALIZATION_STATUS_UNKNOWN) { + status = INITIALIZATION_STATUS_UNKNOWN; + } try { - if (mUpdateStateFinish.getAndSet(true)) { - Slog.w(TAG, "call callback after timeout"); - return; - } - int status = bundle != null ? bundle.getInt( - KEY_INITIALIZATION_STATUS, - INITIALIZATION_STATUS_UNKNOWN) - : INITIALIZATION_STATUS_UNKNOWN; - // Add the protection to avoid unexpected status - if (status > HotwordDetectionService.getMaxCustomInitializationStatus() - && status != INITIALIZATION_STATUS_UNKNOWN) { - status = INITIALIZATION_STATUS_UNKNOWN; - } - callback.onStatusReported(status); + mCallback.onStatusReported(status); } catch (RemoteException e) { Slog.w(TAG, "Failed to report initialization status: " + e); } @@ -214,13 +205,13 @@ final class HotwordDetectionConnection { .whenComplete((res, err) -> { if (err instanceof TimeoutException) { Slog.w(TAG, "updateState timed out"); + if (mUpdateStateAfterStartFinished.getAndSet(true)) { + return; + } try { - if (mUpdateStateFinish.getAndSet(true)) { - return; - } - callback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN); + mCallback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN); } catch (RemoteException e) { - Slog.w(TAG, "Failed to report initialization status: " + e); + Slog.w(TAG, "Failed to report initialization status UNKNOWN", e); } } else if (err != null) { Slog.w(TAG, "Failed to update state: " + err); @@ -230,27 +221,9 @@ final class HotwordDetectionConnection { }); } - private void updateAudioFlinger() { - // TODO: Consider using a proxy that limits the exposed API surface. - IBinder audioFlinger = ServiceManager.getService("media.audio_flinger"); - if (audioFlinger == null) { - throw new IllegalStateException("Service media.audio_flinger wasn't found."); - } - mRemoteHotwordDetectionService.post(service -> service.updateAudioFlinger(audioFlinger)); - } - - private void updateContentCaptureManager() { - IBinder b = ServiceManager - .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); - IContentCaptureManager binderService = IContentCaptureManager.Stub.asInterface(b); - mRemoteHotwordDetectionService.post( - service -> service.updateContentCaptureManager(binderService, - new ContentCaptureOptions(null))); - } - private boolean isBound() { synchronized (mLock) { - return mBound; + return mRemoteHotwordDetectionService.isBound(); } } @@ -258,18 +231,25 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "cancelLocked"); } - if (mBound) { + if (mRemoteHotwordDetectionService.isBound()) { mRemoteHotwordDetectionService.unbind(); - mBound = false; LocalServices.getService(PermissionManagerServiceInternal.class) .setHotwordDetectionServiceProvider(null); mIdentity = null; } + mCancellationTaskFuture.cancel(/* may interrupt */ true); } void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) { - mRemoteHotwordDetectionService.run( - service -> service.updateState(options, sharedMemory, null /* callback */)); + // Prevent doing the init late, so restart is handled equally to a clean process start. + // TODO(b/191742511): this logic needs a test + if (!mUpdateStateAfterStartFinished.get() + && Instant.now().minus(MAX_UPDATE_TIMEOUT_DURATION).isBefore(mLastRestartInstant)) { + updateStateAfterProcessStart(options, sharedMemory); + } else { + mRemoteHotwordDetectionService.run( + service -> service.updateState(options, sharedMemory, null /* callback */)); + } } void startListeningFromMic( @@ -278,7 +258,20 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "startListeningFromMic"); } + mSoftwareCallback = callback; + + synchronized (mLock) { + if (mPerformingSoftwareHotwordDetection) { + Slog.i(TAG, "Hotword validation is already in progress, ignoring."); + return; + } + mPerformingSoftwareHotwordDetection = true; + + startListeningFromMicLocked(); + } + } + private void startListeningFromMicLocked() { // TODO: consider making this a non-anonymous class. IDspHotwordDetectionCallback internalCallback = new IDspHotwordDetectionCallback.Stub() { @Override @@ -286,15 +279,22 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "onDetected"); } - callback.onDetected(result, null, null); + synchronized (mLock) { + if (mPerformingSoftwareHotwordDetection) { + mSoftwareCallback.onDetected(result, null, null); + mPerformingSoftwareHotwordDetection = false; + } else { + Slog.i(TAG, "Hotword detection has already completed"); + } + } } @Override public void onRejected(HotwordRejectedResult result) throws RemoteException { if (DEBUG) { - Slog.d(TAG, "onRejected"); + Slog.wtf(TAG, "onRejected"); } - // onRejected isn't allowed here + // onRejected isn't allowed here, and we are not expecting it. } }; @@ -315,6 +315,7 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "startListeningFromExternalSource"); } + handleExternalSourceHotwordDetection( audioStream, audioFormat, @@ -326,16 +327,25 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "stopListening"); } + synchronized (mLock) { + stopListeningLocked(); + } + } - mRemoteHotwordDetectionService.run(service -> service.stopDetection()); + private void stopListeningLocked() { + if (!mPerformingSoftwareHotwordDetection) { + Slog.i(TAG, "Hotword detection is not running"); + return; + } + mPerformingSoftwareHotwordDetection = false; - synchronized (mLock) { - if (mCurrentAudioSink != null) { - Slog.i(TAG, "Closing audio stream to hotword detector: stopping requested"); - bestEffortClose(mCurrentAudioSink); - } - mCurrentAudioSink = null; + mRemoteHotwordDetectionService.run(IHotwordDetectionService::stopDetection); + + if (mCurrentAudioSink != null) { + Slog.i(TAG, "Closing audio stream to hotword detector: stopping requested"); + bestEffortClose(mCurrentAudioSink); } + mCurrentAudioSink = null; } void triggerHardwareRecognitionEventForTestLocked( @@ -358,7 +368,14 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "onDetected"); } - externalCallback.onKeyphraseDetected(recognitionEvent, result); + synchronized (mLock) { + if (mValidatingDspTrigger) { + mValidatingDspTrigger = false; + externalCallback.onKeyphraseDetected(recognitionEvent, result); + } else { + Slog.i(TAG, "Ignored hotword detected since trigger has been handled"); + } + } } @Override @@ -366,16 +383,26 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "onRejected"); } - externalCallback.onRejected(result); + synchronized (mLock) { + if (mValidatingDspTrigger) { + mValidatingDspTrigger = false; + externalCallback.onRejected(result); + } else { + Slog.i(TAG, "Ignored hotword rejected since trigger has been handled"); + } + } } }; - mRemoteHotwordDetectionService.run( - service -> service.detectFromDspSource( - recognitionEvent, - recognitionEvent.getCaptureFormat(), - VALIDATION_TIMEOUT_MILLIS, - internalCallback)); + synchronized (mLock) { + mValidatingDspTrigger = true; + mRemoteHotwordDetectionService.run( + service -> service.detectFromDspSource( + recognitionEvent, + recognitionEvent.getCaptureFormat(), + VALIDATION_TIMEOUT_MILLIS, + internalCallback)); + } } private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent, @@ -391,7 +418,14 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "onDetected"); } - externalCallback.onKeyphraseDetected(recognitionEvent, result); + synchronized (mLock) { + if (!mValidatingDspTrigger) { + Slog.i(TAG, "Ignoring #onDetected due to a process restart"); + return; + } + mValidatingDspTrigger = false; + externalCallback.onKeyphraseDetected(recognitionEvent, result); + } } @Override @@ -399,16 +433,88 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "onRejected"); } - externalCallback.onRejected(result); + synchronized (mLock) { + if (!mValidatingDspTrigger) { + Slog.i(TAG, "Ignoring #onRejected due to a process restart"); + return; + } + mValidatingDspTrigger = false; + externalCallback.onRejected(result); + } } }; - mRemoteHotwordDetectionService.run( - service -> service.detectFromDspSource( - recognitionEvent, - recognitionEvent.getCaptureFormat(), - VALIDATION_TIMEOUT_MILLIS, - internalCallback)); + synchronized (mLock) { + mValidatingDspTrigger = true; + mRemoteHotwordDetectionService.run( + service -> service.detectFromDspSource( + recognitionEvent, + recognitionEvent.getCaptureFormat(), + VALIDATION_TIMEOUT_MILLIS, + internalCallback)); + } + } + + void forceRestart() { + if (DEBUG) { + Slog.i(TAG, "Requested to restart the service internally. Performing the restart"); + } + synchronized (mLock) { + restartProcessLocked(); + } + } + + private void restartProcessLocked() { + if (DEBUG) { + Slog.i(TAG, "Restarting hotword detection process"); + } + + ServiceConnection oldConnection = mRemoteHotwordDetectionService; + + // TODO(volnov): this can be done after connect() has been successful. + if (mValidatingDspTrigger) { + // We're restarting the process while it's processing a DSP trigger, so report a + // rejection. This also allows the Interactor to startReco again + try { + mCallback.onRejected(new HotwordRejectedResult.Builder().build()); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call #rejected"); + } + mValidatingDspTrigger = false; + } + + mUpdateStateAfterStartFinished.set(false); + mLastRestartInstant = Instant.now(); + + // Recreate connection to reset the cache. + mRemoteHotwordDetectionService = mServiceConnectionFactory.create(); + + if (DEBUG) { + Slog.i(TAG, "Started the new process, issuing #onProcessRestarted"); + } + try { + mCallback.onProcessRestarted(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to communicate #onProcessRestarted", e); + } + + // Restart listening from microphone if the hotword process has been restarted. + if (mPerformingSoftwareHotwordDetection) { + Slog.i(TAG, "Process restarted: calling startRecognition() again"); + startListeningFromMicLocked(); + } + + if (mCurrentAudioSink != null) { + Slog.i(TAG, "Closing external audio stream to hotword detector: process restarted"); + bestEffortClose(mCurrentAudioSink); + mCurrentAudioSink = null; + } + + if (DEBUG) { + Slog.i(TAG, "#onProcessRestarted called, unbinding from the old process"); + } + oldConnection.ignoreConnectionStatusEvents(); + oldConnection.unbind(); } static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub { @@ -462,139 +568,13 @@ final class HotwordDetectionConnection { } } - // TODO: figure out if we need to let the client configure some of the parameters. - private static AudioRecord createAudioRecord( - @NonNull SoundTrigger.KeyphraseRecognitionEvent recognitionEvent) { - int sampleRate = recognitionEvent.getCaptureFormat().getSampleRate(); - return new AudioRecord( - new AudioAttributes.Builder() - .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build(), - recognitionEvent.getCaptureFormat(), - getBufferSizeInBytes( - sampleRate, - MAX_STREAMING_SECONDS, - recognitionEvent.getCaptureFormat().getChannelCount()), - recognitionEvent.getCaptureSession()); - } - - @Nullable - private AudioRecord createMicAudioRecord(AudioFormat audioFormat) { - if (DEBUG) { - Slog.i(TAG, "#createAudioRecord"); - } - try { - AudioRecord audioRecord = new AudioRecord( - new AudioAttributes.Builder() - .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build(), - audioFormat, - getBufferSizeInBytes( - audioFormat.getSampleRate(), - MICROPHONE_BUFFER_LENGTH_SECONDS, - audioFormat.getChannelCount()), - AudioManager.AUDIO_SESSION_ID_GENERATE); - - if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { - Slog.w(TAG, "Failed to initialize AudioRecord"); - audioRecord.release(); - return null; - } - - return audioRecord; - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Failed to create AudioRecord", e); - return null; - } - } - - @Nullable - private AudioRecord createFakeAudioRecord() { - if (DEBUG) { - Slog.i(TAG, "#createFakeAudioRecord"); - } - try { - AudioRecord audioRecord = new AudioRecord.Builder() - .setAudioFormat(new AudioFormat.Builder() - .setSampleRate(32000) - .setEncoding(AudioFormat.ENCODING_PCM_16BIT) - .setChannelMask(AudioFormat.CHANNEL_IN_MONO).build()) - .setAudioAttributes(new AudioAttributes.Builder() - .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build()) - .setBufferSizeInBytes( - AudioRecord.getMinBufferSize(32000, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT) * 2) - .build(); - - if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { - Slog.w(TAG, "Failed to initialize AudioRecord"); - audioRecord.release(); - return null; - } - return audioRecord; - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Failed to create AudioRecord", e); - } - return null; - } - - /** - * Returns the number of bytes required to store {@code bufferLengthSeconds} of audio sampled at - * {@code sampleRate} Hz, using the format returned by DSP audio capture. - */ - private static int getBufferSizeInBytes( - int sampleRate, int bufferLengthSeconds, int intChannelCount) { - return BYTES_PER_SAMPLE * sampleRate * bufferLengthSeconds * intChannelCount; - } - - private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() { - ParcelFileDescriptor[] fileDescriptors; - try { - fileDescriptors = ParcelFileDescriptor.createPipe(); - } catch (IOException e) { - Slog.e(TAG, "Failed to create audio stream pipe", e); - return null; - } - - return Pair.create(fileDescriptors[0], fileDescriptors[1]); - } - public void dump(String prefix, PrintWriter pw) { - pw.print(prefix); pw.print("mBound="); pw.println(mBound); - } - - private interface AudioReader extends Closeable { - int read(byte[] dest, int offset, int length) throws IOException; - - static AudioReader createFromInputStream(InputStream is) { - return new AudioReader() { - @Override - public int read(byte[] dest, int offset, int length) throws IOException { - return is.read(dest, offset, length); - } - - @Override - public void close() throws IOException { - is.close(); - } - }; - } - - static AudioReader createFromAudioRecord(AudioRecord record) { - record.startRecording(); - - return new AudioReader() { - @Override - public int read(byte[] dest, int offset, int length) throws IOException { - return record.read(dest, offset, length); - } - - @Override - public void close() throws IOException { - record.stop(); - record.release(); - } - }; - } + pw.print(prefix); + pw.print("mBound=" + mRemoteHotwordDetectionService.isBound()); + pw.print(", mValidatingDspTrigger=" + mValidatingDspTrigger); + pw.print(", mPerformingSoftwareHotwordDetection=" + mPerformingSoftwareHotwordDetection); + pw.print(", mRestartCount=" + mServiceConnectionFactory.mRestartCount); + pw.println(", mLastRestartInstant=" + mLastRestartInstant); } private void handleExternalSourceHotwordDetection( @@ -605,8 +585,7 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "#handleExternalSourceHotwordDetection"); } - AudioReader audioSource = AudioReader.createFromInputStream( - new ParcelFileDescriptor.AutoCloseInputStream(audioStream)); + InputStream audioSource = new ParcelFileDescriptor.AutoCloseInputStream(audioStream); Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe(); if (clientPipe == null) { @@ -621,7 +600,7 @@ final class HotwordDetectionConnection { } mAudioCopyExecutor.execute(() -> { - try (AudioReader source = audioSource; + try (InputStream source = audioSource; OutputStream fos = new ParcelFileDescriptor.AutoCloseOutputStream(serviceAudioSink)) { @@ -681,6 +660,150 @@ final class HotwordDetectionConnection { })); } + private class ServiceConnectionFactory { + private final Intent mIntent; + private final int mBindingFlags; + + private int mRestartCount = 0; + + ServiceConnectionFactory(@NonNull Intent intent, boolean bindInstantServiceAllowed) { + mIntent = intent; + mBindingFlags = bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0; + } + + ServiceConnection create() { + ServiceConnection connection = + new ServiceConnection(mContext, mIntent, mBindingFlags, mUser, + IHotwordDetectionService.Stub::asInterface, ++mRestartCount); + connection.connect(); + + updateAudioFlinger(connection); + updateContentCaptureManager(connection); + return connection; + } + } + + private class ServiceConnection extends ServiceConnector.Impl<IHotwordDetectionService> { + private final Object mLock = new Object(); + + private final Intent mIntent; + private final int mBindingFlags; + private final int mInstanceNumber; + + private boolean mRespectServiceConnectionStatusChanged = true; + private boolean mIsBound = false; + + ServiceConnection(@NonNull Context context, + @NonNull Intent intent, int bindingFlags, int userId, + @Nullable Function<IBinder, IHotwordDetectionService> binderAsInterface, + int instanceNumber) { + super(context, intent, bindingFlags, userId, binderAsInterface); + this.mIntent = intent; + this.mBindingFlags = bindingFlags; + this.mInstanceNumber = instanceNumber; + } + + @Override // from ServiceConnector.Impl + protected void onServiceConnectionStatusChanged(IHotwordDetectionService service, + boolean connected) { + if (DEBUG) { + Slog.d(TAG, "onServiceConnectionStatusChanged connected = " + connected); + } + synchronized (mLock) { + if (!mRespectServiceConnectionStatusChanged) { + if (DEBUG) { + Slog.d(TAG, "Ignored onServiceConnectionStatusChanged event"); + } + return; + } + mIsBound = connected; + } + } + + @Override + protected long getAutoDisconnectTimeoutMs() { + return -1; + } + + @Override + public void binderDied() { + super.binderDied(); + synchronized (mLock) { + if (!mRespectServiceConnectionStatusChanged) { + if (DEBUG) { + Slog.d(TAG, "Ignored #binderDied event"); + } + return; + } + + Slog.w(TAG, "binderDied"); + try { + mCallback.onError(-1); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to report onError status: " + e); + } + } + } + + @Override + protected boolean bindService( + @NonNull android.content.ServiceConnection serviceConnection) { + try { + return mContext.bindIsolatedService( + mIntent, + Context.BIND_AUTO_CREATE | mBindingFlags, + "hotword_detector_" + mInstanceNumber, + mExecutor, + serviceConnection); + } catch (IllegalArgumentException e) { + Slog.wtf(TAG, "Can't bind to the hotword detection service!", e); + return false; + } + } + + boolean isBound() { + synchronized (mLock) { + return mIsBound; + } + } + + void ignoreConnectionStatusEvents() { + synchronized (mLock) { + mRespectServiceConnectionStatusChanged = false; + } + } + } + + private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() { + ParcelFileDescriptor[] fileDescriptors; + try { + fileDescriptors = ParcelFileDescriptor.createPipe(); + } catch (IOException e) { + Slog.e(TAG, "Failed to create audio stream pipe", e); + return null; + } + + return Pair.create(fileDescriptors[0], fileDescriptors[1]); + } + + private static void updateAudioFlinger(ServiceConnection connection) { + // TODO: Consider using a proxy that limits the exposed API surface. + IBinder audioFlinger = ServiceManager.getService("media.audio_flinger"); + if (audioFlinger == null) { + throw new IllegalStateException("Service media.audio_flinger wasn't found."); + } + connection.post(service -> service.updateAudioFlinger(audioFlinger)); + } + + private static void updateContentCaptureManager(ServiceConnection connection) { + IBinder b = ServiceManager + .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); + IContentCaptureManager binderService = IContentCaptureManager.Stub.asInterface(b); + connection.post( + service -> service.updateContentCaptureManager(binderService, + new ContentCaptureOptions(null))); + } + private static void bestEffortClose(Closeable closeable) { try { closeable.close(); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index a14912e5593d..162acba8002d 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -562,11 +562,10 @@ public class VoiceInteractionManagerService extends SystemService { } void switchImplementationIfNeededLocked(boolean force) { - if (!mCurUserSupported || mTemporarilyDisabled) { + if (!mCurUserSupported) { if (DEBUG_USER) { Slog.d(TAG, "switchImplementationIfNeeded(): skipping: force= " + force - + "mCurUserSupported=" + mCurUserSupported - + "mTemporarilyDisabled=" + mTemporarilyDisabled); + + "mCurUserSupported=" + mCurUserSupported); } if (mImpl != null) { mImpl.shutdownLocked(); @@ -796,6 +795,10 @@ public class VoiceInteractionManagerService extends SystemService { Settings.Secure.ASSISTANT, null, userHandle); } + void forceRestartHotwordDetector() { + mImpl.forceRestartHotwordDetector(); + } + @Override public void showSession(Bundle args, int flags) { synchronized (this) { @@ -1048,13 +1051,16 @@ public class VoiceInteractionManagerService extends SystemService { if (DEBUG) Slog.d(TAG, "setDisabled(): already " + disabled); return; } - Slog.i(TAG, "setDisabled(): changing to " + disabled); - final long caller = Binder.clearCallingIdentity(); - try { - mTemporarilyDisabled = disabled; - switchImplementationIfNeeded(/* force= */ false); - } finally { - Binder.restoreCallingIdentity(caller); + mTemporarilyDisabled = disabled; + if (mTemporarilyDisabled) { + Slog.i(TAG, "setDisabled(): temporarily disabling and hiding current session"); + try { + hideCurrentSession(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to call hideCurrentSession", e); + } + } else { + Slog.i(TAG, "setDisabled(): re-enabling"); } } } @@ -1508,12 +1514,20 @@ public class VoiceInteractionManagerService extends SystemService { public boolean showSessionForActiveService(Bundle args, int sourceFlags, IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) { enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); + if (DEBUG_USER) Slog.d(TAG, "showSessionForActiveService()"); + synchronized (this) { if (mImpl == null) { Slog.w(TAG, "showSessionForActiveService without running voice interaction" + "service"); return false; } + if (mTemporarilyDisabled) { + Slog.i(TAG, "showSessionForActiveService(): ignored while temporarily " + + "disabled"); + return false; + } + final long caller = Binder.clearCallingIdentity(); try { return mImpl.showSessionLocked(args, @@ -1530,22 +1544,21 @@ public class VoiceInteractionManagerService extends SystemService { @Override public void hideCurrentSession() throws RemoteException { enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); - synchronized (this) { - if (mImpl == null) { - return; - } - final long caller = Binder.clearCallingIdentity(); - try { - if (mImpl.mActiveSession != null && mImpl.mActiveSession.mSession != null) { - try { - mImpl.mActiveSession.mSession.closeSystemDialogs(); - } catch (RemoteException e) { - Log.w(TAG, "Failed to call closeSystemDialogs", e); - } + + if (mImpl == null) { + return; + } + final long caller = Binder.clearCallingIdentity(); + try { + if (mImpl.mActiveSession != null && mImpl.mActiveSession.mSession != null) { + try { + mImpl.mActiveSession.mSession.closeSystemDialogs(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to call closeSystemDialogs", e); } - } finally { - Binder.restoreCallingIdentity(caller); } + } finally { + Binder.restoreCallingIdentity(caller); } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index ca30bc51e046..89c5a720ee7e 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -562,6 +562,14 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0; } + void forceRestartHotwordDetector() { + if (mHotwordDetectionConnection == null) { + Slog.w(TAG, "Failed to force-restart hotword detection: no hotword detection active"); + return; + } + mHotwordDetectionConnection.forceRestart(); + } + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { if (!mValid) { pw.print(" NOT VALID: "); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java index 6c355a3b4b30..cdd8f7b91d9d 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java @@ -54,6 +54,8 @@ final class VoiceInteractionManagerServiceShellCommand extends ShellCommand { return requestHide(pw); case "disable": return requestDisable(pw); + case "restart-detection": + return requestRestartDetection(pw); default: return handleDefaultCommands(cmd); } @@ -71,8 +73,11 @@ final class VoiceInteractionManagerServiceShellCommand extends ShellCommand { pw.println(""); pw.println(" hide"); pw.println(" Hides the current session"); + pw.println(""); pw.println(" disable [true|false]"); pw.println(" Temporarily disable (when true) service"); + pw.println(" restart-detection"); + pw.println(" Force a restart of a hotword detection service"); pw.println(""); } } @@ -142,6 +147,16 @@ final class VoiceInteractionManagerServiceShellCommand extends ShellCommand { return 0; } + private int requestRestartDetection(PrintWriter pw) { + Slog.i(TAG, "requestRestartDetection()"); + try { + mService.forceRestartHotwordDetector(); + } catch (Exception e) { + return handleError(pw, "requestRestartDetection()", e); + } + return 0; + } + private static int handleError(PrintWriter pw, String message, Exception e) { Slog.e(TAG, "error calling " + message, e); pw.printf("Error calling %s: %s\n", message, e); diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java index 45d3dade82b6..9ffb236d3f59 100644 --- a/tests/Internal/src/android/app/WallpaperColorsTest.java +++ b/tests/Internal/src/android/app/WallpaperColorsTest.java @@ -20,6 +20,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.os.Parcel; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -106,4 +107,26 @@ public class WallpaperColorsTest { // This would crash: canvas.drawBitmap(image, 0, 0, new Paint()); } + + /** + * Parcelled WallpaperColors object should equal the original. + */ + @Test + public void testParcelUnparcel() { + Bitmap image = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888); + WallpaperColors colors = WallpaperColors.fromBitmap(image); + Parcel parcel = Parcel.obtain(); + colors.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + WallpaperColors reconstructed = new WallpaperColors(parcel); + parcel.recycle(); + Assert.assertEquals("WallpaperColors recreated from Parcel should equal original", + colors, reconstructed); + Assert.assertEquals("getAllColors() on WallpaperColors recreated from Parcel should" + + "return the same as the original", + colors.getAllColors(), reconstructed.getAllColors()); + Assert.assertEquals("getMainColors() on WallpaperColors recreated from Parcel should" + + "return the same as the original", + colors.getMainColors(), reconstructed.getMainColors()); + } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java index f681ee19ab12..5d2f9d748581 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java @@ -242,6 +242,27 @@ public class VcnTest { verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(VCN_STATUS_CODE_SAFE_MODE); } + @Test + public void testSubscriptionSnapshotUpdatesMobileDataState() { + final NetworkRequestListener requestListener = verifyAndGetRequestListener(); + startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]); + + // Expect mobile data enabled from setUp() + assertTrue(mVcn.isMobileDataEnabled()); + + final TelephonySubscriptionSnapshot updatedSnapshot = + mock(TelephonySubscriptionSnapshot.class); + doReturn(TEST_SUB_IDS_IN_GROUP) + .when(updatedSnapshot) + .getAllSubIdsInGroup(eq(TEST_SUB_GROUP)); + doReturn(false).when(mTelephonyManager).isDataEnabled(); + + mVcn.updateSubscriptionSnapshot(updatedSnapshot); + mTestLooper.dispatchAll(); + + assertFalse(mVcn.isMobileDataEnabled()); + } + private void triggerVcnRequestListeners(NetworkRequestListener requestListener) { for (final int[] caps : TEST_CAPS) { startVcnGatewayWithCapabilities(requestListener, caps); |